mirror of
https://github.com/bitwarden/jslib
synced 2025-12-11 05:43:18 +00:00
Merge commit '8b2dfc6cdcb8ff5b604364c2ea6d343473aee7cd' into feature/workspaces
# Conflicts: # common/spec/importers/fsecureFskImporter.spec.ts # common/spec/services/cipher.service.spec.ts # package-lock.json # package.json
This commit is contained in:
@@ -7,9 +7,10 @@ root = true
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Set default charset
|
||||
[*.{js,ts,scss,html}]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_size = 2
|
||||
|
||||
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -33,11 +33,10 @@ jobs:
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||
with:
|
||||
node-version: '14'
|
||||
node-version: '16'
|
||||
|
||||
- name: Update NPM
|
||||
- name: Install node-gyp
|
||||
run: |
|
||||
npm install -g npm@7
|
||||
npm install -g node-gyp
|
||||
node-gyp install $(node -v)
|
||||
|
||||
@@ -52,8 +51,8 @@ jobs:
|
||||
- name: Install Node dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Run linter
|
||||
run: npm run lint
|
||||
# - name: Run linter
|
||||
# run: npm run lint
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_
|
||||
4
.husky/pre-commit
Normal file
4
.husky/pre-commit
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
dist
|
||||
3
.prettierrc.json
Normal file
3
.prettierrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"printWidth": 100
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
Common code referenced across Bitwarden JavaScript projects.
|
||||
|
||||
## Requirements
|
||||
* [Node.js](https://nodejs.org) v14.17 or greater
|
||||
* NPM v7
|
||||
* [Node.js](https://nodejs.org) v16.13.1 or greater
|
||||
* NPM v8
|
||||
* Git
|
||||
* node-gyp
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@ import { FolderService } from 'jslib-common/abstractions/folder.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { OrganizationService } from 'jslib-common/abstractions/organization.service';
|
||||
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { Cipher } from 'jslib-common/models/domain/cipher';
|
||||
|
||||
@@ -89,10 +89,10 @@ export class AddEditComponent implements OnInit {
|
||||
constructor(protected cipherService: CipherService, protected folderService: FolderService,
|
||||
protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
|
||||
protected auditService: AuditService, protected stateService: StateService,
|
||||
protected userService: UserService, protected collectionService: CollectionService,
|
||||
protected messagingService: MessagingService, protected eventService: EventService,
|
||||
protected policyService: PolicyService, protected passwordRepromptService: PasswordRepromptService,
|
||||
private logService: LogService) {
|
||||
protected collectionService: CollectionService, protected messagingService: MessagingService,
|
||||
protected eventService: EventService, protected policyService: PolicyService,
|
||||
private logService: LogService, protected passwordRepromptService: PasswordRepromptService,
|
||||
private organizationService: OrganizationService) {
|
||||
this.typeOptions = [
|
||||
{ name: i18nService.t('typeLogin'), value: CipherType.Login },
|
||||
{ name: i18nService.t('typeCard'), value: CipherType.Card },
|
||||
@@ -160,11 +160,11 @@ export class AddEditComponent implements OnInit {
|
||||
if (await this.policyService.policyAppliesToUser(PolicyType.PersonalOwnership)) {
|
||||
this.allowPersonal = false;
|
||||
} else {
|
||||
const myEmail = await this.userService.getEmail();
|
||||
const myEmail = await this.stateService.getEmail();
|
||||
this.ownershipOptions.push({ name: myEmail, value: null });
|
||||
}
|
||||
|
||||
const orgs = await this.userService.getAllOrganizations();
|
||||
const orgs = await this.organizationService.getAll();
|
||||
orgs.sort(Utils.getSortFunction(this.i18nService, 'name')).forEach(o => {
|
||||
if (o.enabled && o.status === OrganizationUserStatusType.Confirmed) {
|
||||
this.ownershipOptions.push({ name: o.name, value: o.id });
|
||||
@@ -193,12 +193,12 @@ export class AddEditComponent implements OnInit {
|
||||
this.title = this.i18nService.t('addItem');
|
||||
}
|
||||
|
||||
const addEditCipherInfo: any = await this.stateService.get<any>('addEditCipherInfo');
|
||||
const addEditCipherInfo: any = await this.stateService.getAddEditCipherInfo();
|
||||
if (addEditCipherInfo != null) {
|
||||
this.cipher = addEditCipherInfo.cipher;
|
||||
this.collectionIds = addEditCipherInfo.collectionIds;
|
||||
}
|
||||
await this.stateService.remove('addEditCipherInfo');
|
||||
await this.stateService.setAddEditCipherInfo(null);
|
||||
|
||||
if (this.cipher == null) {
|
||||
if (this.editMode) {
|
||||
@@ -442,7 +442,7 @@ export class AddEditComponent implements OnInit {
|
||||
}
|
||||
if (this.cipher.organizationId != null) {
|
||||
this.collections = this.writeableCollections.filter(c => c.organizationId === this.cipher.organizationId);
|
||||
const org = await this.userService.getOrganization(this.cipher.organizationId);
|
||||
const org = await this.organizationService.get(this.cipher.organizationId);
|
||||
if (org != null) {
|
||||
this.cipher.organizationUseTotp = org.useTotp;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
import { Cipher } from 'jslib-common/models/domain/cipher';
|
||||
import { ErrorResponse } from 'jslib-common/models/response/errorResponse';
|
||||
@@ -37,9 +37,9 @@ export class AttachmentsComponent implements OnInit {
|
||||
emergencyAccessId?: string = null;
|
||||
|
||||
constructor(protected cipherService: CipherService, protected i18nService: I18nService,
|
||||
protected cryptoService: CryptoService, protected userService: UserService,
|
||||
protected platformUtilsService: PlatformUtilsService, protected apiService: ApiService,
|
||||
protected win: Window, private logService: LogService) { }
|
||||
protected cryptoService: CryptoService, protected platformUtilsService: PlatformUtilsService,
|
||||
protected apiService: ApiService, protected win: Window,
|
||||
protected logService: LogService, protected stateService: StateService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
await this.init();
|
||||
@@ -164,7 +164,7 @@ export class AttachmentsComponent implements OnInit {
|
||||
this.cipher = await this.cipherDomain.decrypt();
|
||||
|
||||
this.hasUpdatedKey = await this.cryptoService.hasEncKey();
|
||||
const canAccessPremium = await this.userService.canAccessPremium();
|
||||
const canAccessPremium = await this.stateService.getCanAccessPremium();
|
||||
this.canAccessAttachments = canAccessPremium || this.cipher.organizationId != null;
|
||||
|
||||
if (!this.canAccessAttachments) {
|
||||
|
||||
@@ -45,7 +45,7 @@ export class AvatarComponent implements OnChanges, OnInit {
|
||||
}
|
||||
|
||||
private async generate() {
|
||||
const enableGravatars = await this.stateService.get<boolean>('enableGravatars');
|
||||
const enableGravatars = await this.stateService.getEnableGravitars();
|
||||
if (enableGravatars && this.email != null) {
|
||||
const hashBytes = await this.cryptoFunctionService.hash(this.email.toLowerCase().trim(), 'md5');
|
||||
const hash = Utils.fromBufferToHex(hashBytes).toLowerCase();
|
||||
|
||||
@@ -6,7 +6,7 @@ import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
import { EncString } from 'jslib-common/models/domain/encString';
|
||||
import { MasterPasswordPolicyOptions } from 'jslib-common/models/domain/masterPasswordPolicyOptions';
|
||||
@@ -29,12 +29,12 @@ export class ChangePasswordComponent implements OnInit {
|
||||
private masterPasswordStrengthTimeout: any;
|
||||
|
||||
constructor(protected i18nService: I18nService, protected cryptoService: CryptoService,
|
||||
protected messagingService: MessagingService, protected userService: UserService,
|
||||
protected passwordGenerationService: PasswordGenerationService,
|
||||
protected platformUtilsService: PlatformUtilsService, protected policyService: PolicyService) { }
|
||||
protected messagingService: MessagingService, protected passwordGenerationService: PasswordGenerationService,
|
||||
protected platformUtilsService: PlatformUtilsService, protected policyService: PolicyService,
|
||||
protected stateService: StateService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.email = await this.userService.getEmail();
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.enforcedPolicyOptions = await this.policyService.getMasterPasswordPolicyOptions();
|
||||
}
|
||||
|
||||
@@ -47,12 +47,12 @@ export class ChangePasswordComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
const email = await this.userService.getEmail();
|
||||
const email = await this.stateService.getEmail();
|
||||
if (this.kdf == null) {
|
||||
this.kdf = await this.userService.getKdf();
|
||||
this.kdf = await this.stateService.getKdfType();
|
||||
}
|
||||
if (this.kdfIterations == null) {
|
||||
this.kdfIterations = await this.userService.getKdfIterations();
|
||||
this.kdfIterations = await this.stateService.getKdfIterations();
|
||||
}
|
||||
const key = await this.cryptoService.makeKey(this.masterPassword, email.trim().toLowerCase(),
|
||||
this.kdf, this.kdfIterations);
|
||||
|
||||
@@ -14,10 +14,7 @@ import { TreeNode } from 'jslib-common/models/domain/treeNode';
|
||||
|
||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
||||
import { FolderService } from 'jslib-common/abstractions/folder.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
@Directive()
|
||||
export class GroupingsComponent {
|
||||
@@ -50,15 +47,12 @@ export class GroupingsComponent {
|
||||
selectedCollectionId: string = null;
|
||||
|
||||
private collapsedGroupings: Set<string>;
|
||||
private collapsedGroupingsKey: string;
|
||||
|
||||
constructor(protected collectionService: CollectionService, protected folderService: FolderService,
|
||||
protected storageService: StorageService, protected userService: UserService) { }
|
||||
protected stateService: StateService) { }
|
||||
|
||||
async load(setLoaded = true) {
|
||||
const userId = await this.userService.getUserId();
|
||||
this.collapsedGroupingsKey = ConstantsService.collapsedGroupingsKey + '_' + userId;
|
||||
const collapsedGroupings = await this.storageService.get<string[]>(this.collapsedGroupingsKey);
|
||||
const collapsedGroupings = await this.stateService.getCollapsedGroupings();
|
||||
if (collapsedGroupings == null) {
|
||||
this.collapsedGroupings = new Set<string>();
|
||||
} else {
|
||||
@@ -149,7 +143,7 @@ export class GroupingsComponent {
|
||||
this.selectedCollectionId = null;
|
||||
}
|
||||
|
||||
collapse(grouping: FolderView | CollectionView, idPrefix = '') {
|
||||
async collapse(grouping: FolderView | CollectionView, idPrefix = '') {
|
||||
if (grouping.id == null) {
|
||||
return;
|
||||
}
|
||||
@@ -159,7 +153,7 @@ export class GroupingsComponent {
|
||||
} else {
|
||||
this.collapsedGroupings.add(id);
|
||||
}
|
||||
this.storageService.save(this.collapsedGroupingsKey, this.collapsedGroupings);
|
||||
await this.stateService.setCollapsedGroupings(this.collapsedGroupings);
|
||||
}
|
||||
|
||||
isCollapsed(grouping: FolderView | CollectionView, idPrefix = '') {
|
||||
|
||||
@@ -11,8 +11,6 @@ import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
|
||||
const IconMap: any = {
|
||||
@@ -37,7 +35,7 @@ export class IconComponent implements OnChanges {
|
||||
|
||||
private iconsUrl: string;
|
||||
|
||||
constructor(environmentService: EnvironmentService, protected stateService: StateService) {
|
||||
constructor(environmentService: EnvironmentService, private stateService: StateService) {
|
||||
this.iconsUrl = environmentService.getIconsUrl();
|
||||
}
|
||||
|
||||
@@ -46,7 +44,7 @@ export class IconComponent implements OnChanges {
|
||||
// to avoid this we reset all state variables.
|
||||
this.image = null;
|
||||
this.fallbackImage = null;
|
||||
this.imageEnabled = !(await this.stateService.get<boolean>(ConstantsService.disableFaviconKey));
|
||||
this.imageEnabled = !(await this.stateService.getDisableFavicon());
|
||||
this.load();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,8 @@ import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
import { EncString } from 'jslib-common/models/domain/encString';
|
||||
import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
|
||||
|
||||
@@ -25,6 +21,7 @@ import { SecretVerificationRequest } from 'jslib-common/models/request/secretVer
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
|
||||
import { HashPurpose } from 'jslib-common/enums/hashPurpose';
|
||||
import { KeySuffixOptions } from 'jslib-common/enums/keySuffixOptions';
|
||||
|
||||
@Directive()
|
||||
export class LockComponent implements OnInit {
|
||||
@@ -41,38 +38,22 @@ export class LockComponent implements OnInit {
|
||||
hideInput: boolean;
|
||||
|
||||
protected successRoute: string = 'vault';
|
||||
protected onSuccessfulSubmit: () => void;
|
||||
protected onSuccessfulSubmit: () => Promise<void>;
|
||||
|
||||
private invalidPinAttempts = 0;
|
||||
private pinSet: [boolean, boolean];
|
||||
|
||||
constructor(protected router: Router, protected i18nService: I18nService,
|
||||
protected platformUtilsService: PlatformUtilsService, protected messagingService: MessagingService,
|
||||
protected userService: UserService, protected cryptoService: CryptoService,
|
||||
protected storageService: StorageService, protected vaultTimeoutService: VaultTimeoutService,
|
||||
protected cryptoService: CryptoService, protected vaultTimeoutService: VaultTimeoutService,
|
||||
protected environmentService: EnvironmentService, protected stateService: StateService,
|
||||
protected apiService: ApiService, private logService: LogService,
|
||||
private keyConnectorService: KeyConnectorService, protected ngZone: NgZone) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.pinSet = await this.vaultTimeoutService.isPinLockSet();
|
||||
this.pinLock = (this.pinSet[0] && this.vaultTimeoutService.pinProtectedKey != null) || this.pinSet[1];
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometricLock = await this.vaultTimeoutService.isBiometricLockSet() &&
|
||||
(await this.cryptoService.hasKeyStored('biometric') || !this.platformUtilsService.supportsSecureStorage());
|
||||
this.biometricText = await this.storageService.get(ConstantsService.biometricText);
|
||||
this.email = await this.userService.getEmail();
|
||||
const usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||
this.hideInput = usesKeyConnector && !this.pinLock;
|
||||
|
||||
// Users with key connector and without biometric or pin has no MP to unlock using
|
||||
if (usesKeyConnector && !(this.biometricLock || this.pinLock)) {
|
||||
await this.vaultTimeoutService.logOut();
|
||||
}
|
||||
|
||||
const webVaultUrl = this.environmentService.getWebVaultUrl();
|
||||
const vaultUrl = webVaultUrl === 'https://vault.bitwarden.com' ? 'https://bitwarden.com' : webVaultUrl;
|
||||
this.webVaultHostname = Utils.getHostname(vaultUrl);
|
||||
this.stateService.activeAccount.subscribe(async _userId => {
|
||||
await this.load();
|
||||
});
|
||||
}
|
||||
|
||||
async submit() {
|
||||
@@ -87,17 +68,17 @@ export class LockComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
const kdf = await this.userService.getKdf();
|
||||
const kdfIterations = await this.userService.getKdfIterations();
|
||||
const kdf = await this.stateService.getKdfType();
|
||||
const kdfIterations = await this.stateService.getKdfIterations();
|
||||
|
||||
if (this.pinLock) {
|
||||
let failed = true;
|
||||
try {
|
||||
if (this.pinSet[0]) {
|
||||
const key = await this.cryptoService.makeKeyFromPin(this.pin, this.email, kdf, kdfIterations,
|
||||
this.vaultTimeoutService.pinProtectedKey);
|
||||
await this.stateService.getDecryptedPinProtected());
|
||||
const encKey = await this.cryptoService.getEncKey(key);
|
||||
const protectedPin = await this.storageService.get<string>(ConstantsService.protectedPin);
|
||||
const protectedPin = await this.stateService.getProtectedPin();
|
||||
const decPin = await this.cryptoService.decryptToUtf8(new EncString(protectedPin), encKey);
|
||||
failed = decPin !== this.pin;
|
||||
if (!failed) {
|
||||
@@ -148,13 +129,13 @@ export class LockComponent implements OnInit {
|
||||
|
||||
if (passwordValid) {
|
||||
if (this.pinSet[0]) {
|
||||
const protectedPin = await this.storageService.get<string>(ConstantsService.protectedPin);
|
||||
const protectedPin = await this.stateService.getProtectedPin();
|
||||
const encKey = await this.cryptoService.getEncKey(key);
|
||||
const decPin = await this.cryptoService.decryptToUtf8(new EncString(protectedPin), encKey);
|
||||
const pinKey = await this.cryptoService.makePinKey(decPin, this.email, kdf, kdfIterations);
|
||||
this.vaultTimeoutService.pinProtectedKey = await this.cryptoService.encrypt(key.key, pinKey);
|
||||
await this.stateService.setDecryptedPinProtected(await this.cryptoService.encrypt(key.key, pinKey));
|
||||
}
|
||||
this.setKeyAndContinue(key);
|
||||
await this.setKeyAndContinue(key);
|
||||
} else {
|
||||
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
|
||||
this.i18nService.t('invalidMasterPassword'));
|
||||
@@ -175,7 +156,7 @@ export class LockComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
const success = (await this.cryptoService.getKey('biometric')) != null;
|
||||
const success = (await this.cryptoService.getKey(KeySuffixOptions.Biometric)) != null;
|
||||
|
||||
if (success) {
|
||||
await this.doContinue();
|
||||
@@ -196,19 +177,40 @@ export class LockComponent implements OnInit {
|
||||
|
||||
private async setKeyAndContinue(key: SymmetricCryptoKey) {
|
||||
await this.cryptoService.setKey(key);
|
||||
this.doContinue();
|
||||
await this.doContinue();
|
||||
}
|
||||
|
||||
private async doContinue() {
|
||||
this.vaultTimeoutService.biometricLocked = false;
|
||||
this.vaultTimeoutService.everBeenUnlocked = true;
|
||||
const disableFavicon = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
|
||||
await this.stateService.save(ConstantsService.disableFaviconKey, !!disableFavicon);
|
||||
await this.stateService.setBiometricLocked(false);
|
||||
await this.stateService.setEverBeenUnlocked(true);
|
||||
const disableFavicon = await this.stateService.getDisableFavicon();
|
||||
await this.stateService.setDisableFavicon(!!disableFavicon);
|
||||
this.messagingService.send('unlocked');
|
||||
if (this.onSuccessfulSubmit != null) {
|
||||
this.onSuccessfulSubmit();
|
||||
await this.onSuccessfulSubmit();
|
||||
} else if (this.router != null) {
|
||||
this.router.navigate([this.successRoute]);
|
||||
}
|
||||
}
|
||||
|
||||
private async load() {
|
||||
this.pinSet = await this.vaultTimeoutService.isPinLockSet();
|
||||
this.pinLock = (this.pinSet[0] && (await this.stateService.getDecryptedPinProtected()) != null) || this.pinSet[1];
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometricLock = await this.vaultTimeoutService.isBiometricLockSet() &&
|
||||
(await this.cryptoService.hasKeyStored(KeySuffixOptions.Biometric) || !this.platformUtilsService.supportsSecureStorage());
|
||||
this.biometricText = await this.stateService.getBiometricText();
|
||||
this.email = await this.stateService.getEmail();
|
||||
const usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||
this.hideInput = usesKeyConnector && !this.pinLock;
|
||||
|
||||
// Users with key connector and without biometric or pin has no MP to unlock using
|
||||
if (usesKeyConnector && !(this.biometricLock || this.pinLock)) {
|
||||
await this.vaultTimeoutService.logOut();
|
||||
}
|
||||
|
||||
const webVaultUrl = this.environmentService.getWebVaultUrl();
|
||||
const vaultUrl = webVaultUrl === 'https://vault.bitwarden.com' ? 'https://bitwarden.com' : webVaultUrl;
|
||||
this.webVaultHostname = Utils.getHostname(vaultUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,19 +19,11 @@ import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
|
||||
import { CaptchaProtectedComponent } from './captchaProtected.component';
|
||||
|
||||
const Keys = {
|
||||
rememberedEmail: 'rememberedEmail',
|
||||
rememberEmail: 'rememberEmail',
|
||||
};
|
||||
|
||||
@Directive()
|
||||
export class LoginComponent extends CaptchaProtectedComponent implements OnInit {
|
||||
@Input() email: string = '';
|
||||
@@ -53,22 +45,19 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
||||
platformUtilsService: PlatformUtilsService, i18nService: I18nService,
|
||||
protected stateService: StateService, environmentService: EnvironmentService,
|
||||
protected passwordGenerationService: PasswordGenerationService,
|
||||
protected cryptoFunctionService: CryptoFunctionService, private storageService: StorageService,
|
||||
protected logService: LogService, protected ngZone: NgZone) {
|
||||
protected cryptoFunctionService: CryptoFunctionService, protected logService: LogService,
|
||||
protected ngZone: NgZone) {
|
||||
super(environmentService, i18nService, platformUtilsService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
if (this.email == null || this.email === '') {
|
||||
this.email = await this.storageService.get<string>(Keys.rememberedEmail);
|
||||
this.email = await this.stateService.getRememberedEmail();
|
||||
if (this.email == null) {
|
||||
this.email = '';
|
||||
}
|
||||
}
|
||||
this.rememberEmail = await this.storageService.get<boolean>(Keys.rememberEmail);
|
||||
if (this.rememberEmail == null) {
|
||||
this.rememberEmail = true;
|
||||
}
|
||||
this.rememberEmail = await this.stateService.getRememberedEmail() != null;
|
||||
if (Utils.isBrowser && !Utils.isNode) {
|
||||
this.focusInput();
|
||||
}
|
||||
@@ -96,11 +85,10 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
||||
try {
|
||||
this.formPromise = this.authService.logIn(this.email, this.masterPassword, this.captchaToken);
|
||||
const response = await this.formPromise;
|
||||
await this.storageService.save(Keys.rememberEmail, this.rememberEmail);
|
||||
if (this.rememberEmail) {
|
||||
await this.storageService.save(Keys.rememberedEmail, this.email);
|
||||
await this.stateService.setRememberedEmail(this.email);
|
||||
} else {
|
||||
await this.storageService.remove(Keys.rememberedEmail);
|
||||
await this.stateService.setRememberedEmail(null);
|
||||
}
|
||||
if (this.handleCaptchaRequired(response)) {
|
||||
return;
|
||||
@@ -117,8 +105,8 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
||||
this.router.navigate([this.forcePasswordResetRoute]);
|
||||
}
|
||||
} else {
|
||||
const disableFavicon = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
|
||||
await this.stateService.save(ConstantsService.disableFaviconKey, !!disableFavicon);
|
||||
const disableFavicon = await this.stateService.getDisableFavicon();
|
||||
await this.stateService.setDisableFavicon(!!disableFavicon);
|
||||
if (this.onSuccessfulLogin != null) {
|
||||
this.onSuccessfulLogin();
|
||||
}
|
||||
@@ -158,8 +146,8 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
||||
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
||||
|
||||
// Save sso params
|
||||
await this.storageService.save(ConstantsService.ssoStateKey, state);
|
||||
await this.storageService.save(ConstantsService.ssoCodeVerifierKey, ssoCodeVerifier);
|
||||
await this.stateService.setSsoState(state);
|
||||
await this.stateService.setSsoCodeVerifier(ssoCodeVerifier);
|
||||
|
||||
// Build URI
|
||||
const webUrl = this.environmentService.getWebVaultUrl();
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
@Directive()
|
||||
export class PremiumComponent implements OnInit {
|
||||
@@ -13,10 +13,11 @@ export class PremiumComponent implements OnInit {
|
||||
refreshPromise: Promise<any>;
|
||||
|
||||
constructor(protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
|
||||
protected apiService: ApiService, protected userService: UserService, private logService: LogService) { }
|
||||
protected apiService: ApiService, private logService: LogService,
|
||||
protected stateService: StateService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.isPremium = await this.userService.canAccessPremium();
|
||||
this.isPremium = await this.stateService.getCanAccessPremium();
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
@@ -24,7 +25,7 @@ export class PremiumComponent implements OnInit {
|
||||
this.refreshPromise = this.apiService.refreshIdentityToken();
|
||||
await this.refreshPromise;
|
||||
this.platformUtilsService.showToast('success', null, this.i18nService.t('refreshComplete'));
|
||||
this.isPremium = await this.userService.canAccessPremium();
|
||||
this.isPremium = await this.stateService.getCanAccessPremium();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
||||
const request = new RegisterRequest(this.email, this.name, hashedPassword,
|
||||
this.hint, encKey[1].encryptedString, kdf, kdfIterations, this.referenceData, this.captchaToken);
|
||||
request.keys = new KeysRequest(keys[0], keys[1].encryptedString);
|
||||
const orgInvite = await this.stateService.get<any>('orgInvitation');
|
||||
const orgInvite = await this.stateService.getOrganizationInvitation();
|
||||
if (orgInvite != null && orgInvite.token != null && orgInvite.organizationUserId != null) {
|
||||
request.token = orgInvite.token;
|
||||
request.organizationUserId = orgInvite.organizationUserId;
|
||||
|
||||
@@ -8,11 +8,8 @@ import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
import { Organization } from 'jslib-common/models/domain/organization';
|
||||
|
||||
@@ -27,14 +24,14 @@ export class RemovePasswordComponent implements OnInit {
|
||||
organization: Organization;
|
||||
email: string;
|
||||
|
||||
constructor(private router: Router, private userService: UserService,
|
||||
constructor(private router: Router, private stateService: StateService,
|
||||
private apiService: ApiService, private syncService: SyncService,
|
||||
private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
|
||||
private keyConnectorService: KeyConnectorService, private storageService: StorageService) { }
|
||||
private keyConnectorService: KeyConnectorService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.organization = await this.keyConnectorService.getManagingOrganization();
|
||||
this.email = await this.userService.getEmail();
|
||||
this.email = await this.stateService.getEmail();
|
||||
await this.syncService.fullSync(false);
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
import { SendFileView } from 'jslib-common/models/view/sendFileView';
|
||||
import { SendTextView } from 'jslib-common/models/view/sendTextView';
|
||||
@@ -57,9 +57,9 @@ export class AddEditComponent implements OnInit {
|
||||
|
||||
constructor(protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
|
||||
protected environmentService: EnvironmentService, protected datePipe: DatePipe,
|
||||
protected sendService: SendService, protected userService: UserService,
|
||||
protected messagingService: MessagingService, protected policyService: PolicyService,
|
||||
private logService: LogService) {
|
||||
protected sendService: SendService, protected messagingService: MessagingService,
|
||||
protected policyService: PolicyService, private logService: LogService,
|
||||
protected stateService: StateService) {
|
||||
this.typeOptions = [
|
||||
{ name: i18nService.t('sendTypeFile'), value: SendType.File },
|
||||
{ name: i18nService.t('sendTypeText'), value: SendType.Text },
|
||||
@@ -108,8 +108,8 @@ export class AddEditComponent implements OnInit {
|
||||
this.disableHideEmail = await this.policyService.policyAppliesToUser(PolicyType.SendOptions,
|
||||
p => p.data.disableHideEmail);
|
||||
|
||||
this.canAccessPremium = await this.userService.canAccessPremium();
|
||||
this.emailVerified = await this.userService.getEmailVerified();
|
||||
this.canAccessPremium = await this.stateService.getCanAccessPremium();
|
||||
this.emailVerified = await this.stateService.getEmailVerified();
|
||||
if (!this.canAccessPremium || !this.emailVerified) {
|
||||
this.type = SendType.Text;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.se
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { SearchService } from 'jslib-common/abstractions/search.service';
|
||||
import { SendService } from 'jslib-common/abstractions/send.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
@Directive()
|
||||
export class SendComponent implements OnInit {
|
||||
@@ -48,8 +47,7 @@ export class SendComponent implements OnInit {
|
||||
constructor(protected sendService: SendService, protected i18nService: I18nService,
|
||||
protected platformUtilsService: PlatformUtilsService, protected environmentService: EnvironmentService,
|
||||
protected ngZone: NgZone, protected searchService: SearchService,
|
||||
protected policyService: PolicyService, protected userService: UserService,
|
||||
private logService: LogService) { }
|
||||
protected policyService: PolicyService, private logService: LogService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.disableSend = await this.policyService.policyAppliesToUser(PolicyType.DisableSend);
|
||||
|
||||
@@ -13,8 +13,8 @@ import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { EncString } from 'jslib-common/models/domain/encString';
|
||||
import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
|
||||
@@ -42,12 +42,13 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
onSuccessfulChangePassword: () => Promise<any>;
|
||||
successRoute = 'vault';
|
||||
|
||||
constructor(i18nService: I18nService, cryptoService: CryptoService, messagingService: MessagingService,
|
||||
userService: UserService, passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService, policyService: PolicyService, protected router: Router,
|
||||
private apiService: ApiService, private syncService: SyncService, private route: ActivatedRoute) {
|
||||
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
|
||||
platformUtilsService, policyService);
|
||||
constructor(i18nService: I18nService, cryptoService: CryptoService,
|
||||
messagingService: MessagingService, passwordGenerationService: PasswordGenerationService,
|
||||
platformUtilsService: PlatformUtilsService, policyService: PolicyService,
|
||||
protected router: Router, private apiService: ApiService,
|
||||
private syncService: SyncService, private route: ActivatedRoute, stateService: StateService) {
|
||||
super(i18nService, cryptoService, messagingService, passwordGenerationService,
|
||||
platformUtilsService, policyService, stateService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -102,7 +103,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
if (response == null) {
|
||||
throw new Error(this.i18nService.t('resetPasswordOrgKeysError'));
|
||||
}
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
const publicKey = Utils.fromB64ToArray(response.publicKey);
|
||||
|
||||
// RSA Encrypt user's encKey.key with organization public key
|
||||
@@ -138,8 +139,8 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
}
|
||||
|
||||
private async onSetPasswordSuccess(key: SymmetricCryptoKey, encKey: [SymmetricCryptoKey, EncString], keys: [string, EncString]) {
|
||||
await this.userService.setInformation(await this.userService.getUserId(), await this.userService.getEmail(),
|
||||
this.kdf, this.kdfIterations);
|
||||
await this.stateService.setKdfType(this.kdf);
|
||||
await this.stateService.setKdfIterations(this.kdfIterations);
|
||||
await this.cryptoService.setKey(key);
|
||||
await this.cryptoService.setEncKey(encKey[1].encryptedString);
|
||||
await this.cryptoService.setEncPrivateKey(keys[1].encryptedString);
|
||||
|
||||
@@ -5,11 +5,7 @@ import {
|
||||
|
||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
|
||||
@@ -17,15 +13,13 @@ import { ModalRef } from './modal/modal.ref';
|
||||
|
||||
@Directive()
|
||||
export class SetPinComponent implements OnInit {
|
||||
|
||||
pin = '';
|
||||
showPin = false;
|
||||
masterPassOnRestart = true;
|
||||
showMasterPassOnRestart = true;
|
||||
|
||||
constructor(private modalRef: ModalRef, private cryptoService: CryptoService, private userService: UserService,
|
||||
private storageService: StorageService, private vaultTimeoutService: VaultTimeoutService,
|
||||
private keyConnectorService: KeyConnectorService) { }
|
||||
constructor(private modalRef: ModalRef, private cryptoService: CryptoService,
|
||||
private keyConnectorService: KeyConnectorService, private stateService: StateService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.showMasterPassOnRestart = this.masterPassOnRestart = !await this.keyConnectorService.getUsesKeyConnector();
|
||||
@@ -40,18 +34,18 @@ export class SetPinComponent implements OnInit {
|
||||
this.modalRef.close(false);
|
||||
}
|
||||
|
||||
const kdf = await this.userService.getKdf();
|
||||
const kdfIterations = await this.userService.getKdfIterations();
|
||||
const email = await this.userService.getEmail();
|
||||
const kdf = await this.stateService.getKdfType();
|
||||
const kdfIterations = await this.stateService.getKdfIterations();
|
||||
const email = await this.stateService.getEmail();
|
||||
const pinKey = await this.cryptoService.makePinKey(this.pin, email, kdf, kdfIterations);
|
||||
const key = await this.cryptoService.getKey();
|
||||
const pinProtectedKey = await this.cryptoService.encrypt(key.key, pinKey);
|
||||
if (this.masterPassOnRestart) {
|
||||
const encPin = await this.cryptoService.encrypt(this.pin);
|
||||
await this.storageService.save(ConstantsService.protectedPin, encPin.encryptedString);
|
||||
this.vaultTimeoutService.pinProtectedKey = pinProtectedKey;
|
||||
await this.stateService.setProtectedPin(encPin.encryptedString);
|
||||
await this.stateService.setDecryptedPinProtected(pinProtectedKey);
|
||||
} else {
|
||||
await this.storageService.save(ConstantsService.pinProtectedKey, pinProtectedKey.encryptedString);
|
||||
await this.stateService.setEncryptedPinProtected(pinProtectedKey.encryptedString);
|
||||
}
|
||||
|
||||
this.modalRef.close(true);
|
||||
|
||||
@@ -12,8 +12,8 @@ import { CipherService } from 'jslib-common/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib-common/abstractions/collection.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { OrganizationService } from 'jslib-common/abstractions/organization.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { Organization } from 'jslib-common/models/domain/organization';
|
||||
import { CipherView } from 'jslib-common/models/view/cipherView';
|
||||
@@ -35,8 +35,8 @@ export class ShareComponent implements OnInit {
|
||||
protected writeableCollections: CollectionView[] = [];
|
||||
|
||||
constructor(protected collectionService: CollectionService, protected platformUtilsService: PlatformUtilsService,
|
||||
protected i18nService: I18nService, protected userService: UserService,
|
||||
protected cipherService: CipherService, private logService: LogService) { }
|
||||
protected i18nService: I18nService, protected cipherService: CipherService,
|
||||
private logService: LogService, protected organizationService: OrganizationService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
await this.load();
|
||||
@@ -45,7 +45,7 @@ export class ShareComponent implements OnInit {
|
||||
async load() {
|
||||
const allCollections = await this.collectionService.getAllDecrypted();
|
||||
this.writeableCollections = allCollections.map(c => c).filter(c => !c.readOnly);
|
||||
const orgs = await this.userService.getAllOrganizations();
|
||||
const orgs = await this.organizationService.getAll();
|
||||
this.organizations = orgs.sort(Utils.getSortFunction(this.i18nService, 'name'))
|
||||
.filter(o => o.enabled && o.status === OrganizationUserStatusType.Confirmed);
|
||||
|
||||
|
||||
@@ -15,9 +15,6 @@ import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
|
||||
@@ -47,18 +44,18 @@ export class SsoComponent {
|
||||
|
||||
constructor(protected authService: AuthService, protected router: Router,
|
||||
protected i18nService: I18nService, protected route: ActivatedRoute,
|
||||
protected storageService: StorageService, protected stateService: StateService,
|
||||
protected platformUtilsService: PlatformUtilsService, protected apiService: ApiService,
|
||||
protected cryptoFunctionService: CryptoFunctionService, protected environmentService: EnvironmentService,
|
||||
protected passwordGenerationService: PasswordGenerationService, protected logService: LogService) { }
|
||||
protected stateService: StateService, protected platformUtilsService: PlatformUtilsService,
|
||||
protected apiService: ApiService, protected cryptoFunctionService: CryptoFunctionService,
|
||||
protected environmentService: EnvironmentService, protected passwordGenerationService: PasswordGenerationService,
|
||||
protected logService: LogService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.route.queryParams.pipe(first()).subscribe(async qParams => {
|
||||
if (qParams.code != null && qParams.state != null) {
|
||||
const codeVerifier = await this.storageService.get<string>(ConstantsService.ssoCodeVerifierKey);
|
||||
const state = await this.storageService.get<string>(ConstantsService.ssoStateKey);
|
||||
await this.storageService.remove(ConstantsService.ssoCodeVerifierKey);
|
||||
await this.storageService.remove(ConstantsService.ssoStateKey);
|
||||
const codeVerifier = await this.stateService.getSsoCodeVerifier();
|
||||
const state = await this.stateService.getSsoState();
|
||||
await this.stateService.setSsoCodeVerifier(null);
|
||||
await this.stateService.setSsoState(null);
|
||||
if (qParams.code != null && codeVerifier != null && state != null && this.checkState(state, qParams.state)) {
|
||||
await this.logIn(qParams.code, codeVerifier, this.getOrgIdentifierFromState(qParams.state));
|
||||
}
|
||||
@@ -106,7 +103,7 @@ export class SsoComponent {
|
||||
const codeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
|
||||
const codeVerifierHash = await this.cryptoFunctionService.hash(codeVerifier, 'sha256');
|
||||
codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
||||
await this.storageService.save(ConstantsService.ssoCodeVerifierKey, codeVerifier);
|
||||
await this.stateService.setSsoCodeVerifier(codeVerifier);
|
||||
}
|
||||
|
||||
if (state == null) {
|
||||
@@ -120,7 +117,7 @@ export class SsoComponent {
|
||||
state += `_identifier=${this.identifier}`;
|
||||
|
||||
// Save state (regardless of new or existing)
|
||||
await this.storageService.save(ConstantsService.ssoStateKey, state);
|
||||
await this.stateService.setSsoState(state);
|
||||
|
||||
let authorizeUrl = this.environmentService.getIdentityUrl() + '/connect/authorize?' +
|
||||
'client_id=' + this.clientId + '&redirect_uri=' + encodeURIComponent(this.redirectUri) + '&' +
|
||||
@@ -170,8 +167,8 @@ export class SsoComponent {
|
||||
this.router.navigate([this.forcePasswordResetRoute]);
|
||||
}
|
||||
} else {
|
||||
const disableFavicon = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
|
||||
await this.stateService.save(ConstantsService.disableFaviconKey, !!disableFavicon);
|
||||
const disableFavicon = await this.stateService.getDisableFavicon();
|
||||
await this.stateService.setDisableFavicon(!!disableFavicon);
|
||||
if (this.onSuccessfulLogin != null) {
|
||||
this.onSuccessfulLogin();
|
||||
}
|
||||
|
||||
@@ -20,10 +20,8 @@ import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
|
||||
import { TwoFactorProviders } from 'jslib-common/services/auth.service';
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
import * as DuoWebSDK from 'duo_web_sdk';
|
||||
import { WebAuthnIFrame } from 'jslib-common/misc/webauthn_iframe';
|
||||
@@ -58,8 +56,7 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
|
||||
protected i18nService: I18nService, protected apiService: ApiService,
|
||||
protected platformUtilsService: PlatformUtilsService, protected win: Window,
|
||||
protected environmentService: EnvironmentService, protected stateService: StateService,
|
||||
protected storageService: StorageService, protected route: ActivatedRoute,
|
||||
protected logService: LogService) {
|
||||
protected route: ActivatedRoute, protected logService: LogService) {
|
||||
this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win);
|
||||
}
|
||||
|
||||
@@ -179,8 +176,8 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
|
||||
async doSubmit() {
|
||||
this.formPromise = this.authService.logInTwoFactor(this.selectedProviderType, this.token, this.remember);
|
||||
const response: AuthResult = await this.formPromise;
|
||||
const disableFavicon = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
|
||||
await this.stateService.save(ConstantsService.disableFaviconKey, !!disableFavicon);
|
||||
const disableFavicon = await this.stateService.getDisableFavicon();
|
||||
await this.stateService.setDisableFavicon(!!disableFavicon);
|
||||
if (this.onSuccessfulLogin != null) {
|
||||
this.onSuccessfulLogin();
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { ChangePasswordComponent as BaseChangePasswordComponent } from './change-password.component';
|
||||
|
||||
@@ -30,11 +30,11 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
||||
|
||||
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService, policyService: PolicyService,
|
||||
cryptoService: CryptoService, userService: UserService,
|
||||
messagingService: MessagingService, private apiService: ApiService,
|
||||
cryptoService: CryptoService, messagingService: MessagingService,
|
||||
private apiService: ApiService, stateService: StateService,
|
||||
private syncService: SyncService, private logService: LogService) {
|
||||
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
|
||||
platformUtilsService, policyService);
|
||||
super(i18nService, cryptoService, messagingService, passwordGenerationService,
|
||||
platformUtilsService, policyService, stateService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -49,9 +49,9 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
||||
|
||||
async setupSubmitActions(): Promise<boolean> {
|
||||
this.enforcedPolicyOptions = await this.policyService.getMasterPasswordPolicyOptions();
|
||||
this.email = await this.userService.getEmail();
|
||||
this.kdf = await this.userService.getKdf();
|
||||
this.kdfIterations = await this.userService.getKdfIterations();
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.kdf = await this.stateService.getKdfType();
|
||||
this.kdfIterations = await this.stateService.getKdfIterations();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { TokenService } from 'jslib-common/abstractions/token.service';
|
||||
import { TotpService } from 'jslib-common/abstractions/totp.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { ErrorResponse } from 'jslib-common/models/response/errorResponse';
|
||||
|
||||
@@ -67,9 +67,9 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
protected cryptoService: CryptoService, protected platformUtilsService: PlatformUtilsService,
|
||||
protected auditService: AuditService, protected win: Window,
|
||||
protected broadcasterService: BroadcasterService, protected ngZone: NgZone,
|
||||
protected changeDetectorRef: ChangeDetectorRef, protected userService: UserService,
|
||||
protected eventService: EventService, protected apiService: ApiService,
|
||||
protected passwordRepromptService: PasswordRepromptService, private logService: LogService) { }
|
||||
protected changeDetectorRef: ChangeDetectorRef, protected eventService: EventService,
|
||||
protected apiService: ApiService, protected passwordRepromptService: PasswordRepromptService,
|
||||
private logService: LogService, protected stateService: StateService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
@@ -96,7 +96,7 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
|
||||
const cipher = await this.cipherService.get(this.cipherId);
|
||||
this.cipher = await cipher.decrypt();
|
||||
this.canAccessPremium = await this.userService.canAccessPremium();
|
||||
this.canAccessPremium = await this.stateService.getCanAccessPremium();
|
||||
|
||||
if (this.cipher.type === CipherType.Login && this.cipher.login.totp &&
|
||||
(cipher.organizationUseTotp || this.canAccessPremium)) {
|
||||
|
||||
@@ -8,16 +8,17 @@ import {
|
||||
|
||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuardService implements CanActivate {
|
||||
constructor(private vaultTimeoutService: VaultTimeoutService, private userService: UserService,
|
||||
private router: Router, private messagingService: MessagingService, private keyConnectorService: KeyConnectorService) { }
|
||||
constructor(private vaultTimeoutService: VaultTimeoutService, private router: Router,
|
||||
private messagingService: MessagingService, private keyConnectorService: KeyConnectorService,
|
||||
private stateService: StateService) { }
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
||||
const isAuthed = await this.userService.isAuthenticated();
|
||||
const isAuthed = await this.stateService.getIsAuthenticated();
|
||||
if (!isAuthed) {
|
||||
this.messagingService.send('authBlocked');
|
||||
return false;
|
||||
|
||||
@@ -19,16 +19,18 @@ import { FileUploadService } from 'jslib-common/services/fileUpload.service';
|
||||
import { FolderService } from 'jslib-common/services/folder.service';
|
||||
import { KeyConnectorService } from 'jslib-common/services/keyConnector.service';
|
||||
import { NotificationsService } from 'jslib-common/services/notifications.service';
|
||||
import { OrganizationService } from 'jslib-common/services/organization.service';
|
||||
import { PasswordGenerationService } from 'jslib-common/services/passwordGeneration.service';
|
||||
import { PolicyService } from 'jslib-common/services/policy.service';
|
||||
import { ProviderService } from 'jslib-common/services/provider.service';
|
||||
import { SearchService } from 'jslib-common/services/search.service';
|
||||
import { SendService } from 'jslib-common/services/send.service';
|
||||
import { SettingsService } from 'jslib-common/services/settings.service';
|
||||
import { StateService } from 'jslib-common/services/state.service';
|
||||
import { StateMigrationService } from 'jslib-common/services/stateMigration.service';
|
||||
import { SyncService } from 'jslib-common/services/sync.service';
|
||||
import { TokenService } from 'jslib-common/services/token.service';
|
||||
import { TotpService } from 'jslib-common/services/totp.service';
|
||||
import { UserService } from 'jslib-common/services/user.service';
|
||||
import { UserVerificationService } from 'jslib-common/services/userVerification.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/services/vaultTimeout.service';
|
||||
import { WebCryptoFunctionService } from 'jslib-common/services/webCryptoFunction.service';
|
||||
@@ -42,7 +44,7 @@ import { CipherService as CipherServiceAbstraction } from 'jslib-common/abstract
|
||||
import { CollectionService as CollectionServiceAbstraction } from 'jslib-common/abstractions/collection.service';
|
||||
import { CryptoService as CryptoServiceAbstraction } from 'jslib-common/abstractions/crypto.service';
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction, Urls } from 'jslib-common/abstractions/environment.service';
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction } from 'jslib-common/abstractions/environment.service';
|
||||
import { EventService as EventServiceAbstraction } from 'jslib-common/abstractions/event.service';
|
||||
import { ExportService as ExportServiceAbstraction } from 'jslib-common/abstractions/export.service';
|
||||
import { FileUploadService as FileUploadServiceAbstraction } from 'jslib-common/abstractions/fileUpload.service';
|
||||
@@ -52,21 +54,23 @@ import { KeyConnectorService as KeyConnectorServiceAbstraction } from 'jslib-com
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { MessagingService as MessagingServiceAbstraction } from 'jslib-common/abstractions/messaging.service';
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from 'jslib-common/abstractions/notifications.service';
|
||||
import { OrganizationService as OrganizationServiceAbstraction } from 'jslib-common/abstractions/organization.service';
|
||||
import {
|
||||
PasswordGenerationService as PasswordGenerationServiceAbstraction,
|
||||
} from 'jslib-common/abstractions/passwordGeneration.service';
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService as PolicyServiceAbstraction } from 'jslib-common/abstractions/policy.service';
|
||||
import { ProviderService as ProviderServiceAbstraction } from 'jslib-common/abstractions/provider.service';
|
||||
import { SearchService as SearchServiceAbstraction } from 'jslib-common/abstractions/search.service';
|
||||
import { SendService as SendServiceAbstraction } from 'jslib-common/abstractions/send.service';
|
||||
import { SettingsService as SettingsServiceAbstraction } from 'jslib-common/abstractions/settings.service';
|
||||
import { StateService as StateServiceAbstraction } from 'jslib-common/abstractions/state.service';
|
||||
import { StateMigrationService as StateMigrationServiceAbstraction } from 'jslib-common/abstractions/stateMigration.service';
|
||||
import { StorageService as StorageServiceAbstraction } from 'jslib-common/abstractions/storage.service';
|
||||
import { SyncService as SyncServiceAbstraction } from 'jslib-common/abstractions/sync.service';
|
||||
import { TokenService as TokenServiceAbstraction } from 'jslib-common/abstractions/token.service';
|
||||
import { TotpService as TotpServiceAbstraction } from 'jslib-common/abstractions/totp.service';
|
||||
import { UserService as UserServiceAbstraction } from 'jslib-common/abstractions/user.service';
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from 'jslib-common/abstractions/userVerification.service';
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
|
||||
@@ -108,7 +112,6 @@ import { ValidationService } from './validation.service';
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
UserServiceAbstraction,
|
||||
TokenServiceAbstraction,
|
||||
AppIdServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
@@ -117,28 +120,41 @@ import { ValidationService } from './validation.service';
|
||||
VaultTimeoutServiceAbstraction,
|
||||
LogService,
|
||||
CryptoFunctionServiceAbstraction,
|
||||
EnvironmentServiceAbstraction,
|
||||
KeyConnectorServiceAbstraction,
|
||||
EnvironmentServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: CipherServiceAbstraction,
|
||||
useFactory: (cryptoService: CryptoServiceAbstraction, userService: UserServiceAbstraction,
|
||||
settingsService: SettingsServiceAbstraction, apiService: ApiServiceAbstraction,
|
||||
fileUploadService: FileUploadServiceAbstraction, storageService: StorageServiceAbstraction,
|
||||
i18nService: I18nServiceAbstraction, injector: Injector, logService: LogService) =>
|
||||
new CipherService(cryptoService, userService, settingsService, apiService, fileUploadService,
|
||||
storageService, i18nService, () => injector.get(SearchServiceAbstraction), logService),
|
||||
useFactory: (
|
||||
cryptoService: CryptoServiceAbstraction,
|
||||
settingsService: SettingsServiceAbstraction,
|
||||
apiService: ApiServiceAbstraction,
|
||||
fileUploadService: FileUploadServiceAbstraction,
|
||||
i18nService: I18nServiceAbstraction,
|
||||
injector: Injector,
|
||||
logService: LogService,
|
||||
stateService: StateServiceAbstraction,
|
||||
) => new CipherService(
|
||||
cryptoService,
|
||||
settingsService,
|
||||
apiService,
|
||||
fileUploadService,
|
||||
i18nService,
|
||||
() => injector.get(SearchServiceAbstraction),
|
||||
logService,
|
||||
stateService,
|
||||
),
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
UserServiceAbstraction,
|
||||
SettingsServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
FileUploadServiceAbstraction,
|
||||
StorageServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
Injector, // TODO: Get rid of this circular dependency!
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -146,11 +162,10 @@ import { ValidationService } from './validation.service';
|
||||
useClass: FolderService,
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
UserServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
StorageServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
CipherServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{ provide: LogService, useFactory: () => new ConsoleLogService(false) },
|
||||
@@ -159,35 +174,33 @@ import { ValidationService } from './validation.service';
|
||||
useClass: CollectionService,
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
UserServiceAbstraction,
|
||||
StorageServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: EnvironmentServiceAbstraction,
|
||||
useClass: EnvironmentService,
|
||||
deps: [StorageServiceAbstraction],
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: TotpServiceAbstraction,
|
||||
useClass: TotpService,
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
CryptoFunctionServiceAbstraction,
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{ provide: TokenServiceAbstraction, useClass: TokenService, deps: [StorageServiceAbstraction] },
|
||||
{ provide: TokenServiceAbstraction, useClass: TokenService, deps: [StateServiceAbstraction] },
|
||||
{
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: CryptoService,
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
'SECURE_STORAGE',
|
||||
CryptoFunctionServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -195,8 +208,8 @@ import { ValidationService } from './validation.service';
|
||||
useClass: PasswordGenerationService,
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
StorageServiceAbstraction,
|
||||
PolicyServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -222,71 +235,121 @@ import { ValidationService } from './validation.service';
|
||||
},
|
||||
{
|
||||
provide: SyncServiceAbstraction,
|
||||
useFactory: (userService: UserServiceAbstraction, apiService: ApiServiceAbstraction,
|
||||
settingsService: SettingsServiceAbstraction, folderService: FolderServiceAbstraction,
|
||||
cipherService: CipherServiceAbstraction, cryptoService: CryptoServiceAbstraction,
|
||||
collectionService: CollectionServiceAbstraction, storageService: StorageServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction, policyService: PolicyServiceAbstraction,
|
||||
sendService: SendServiceAbstraction, logService: LogService, tokenService: TokenService,
|
||||
keyConnectorService: KeyConnectorServiceAbstraction) => new SyncService(userService, apiService,
|
||||
settingsService, folderService, cipherService, cryptoService, collectionService, storageService,
|
||||
messagingService, policyService, sendService, logService, tokenService, keyConnectorService,
|
||||
async (expired: boolean) => messagingService.send('logout', { expired: expired })),
|
||||
useFactory: (
|
||||
apiService: ApiServiceAbstraction,
|
||||
settingsService: SettingsServiceAbstraction,
|
||||
folderService: FolderServiceAbstraction,
|
||||
cipherService: CipherServiceAbstraction,
|
||||
cryptoService: CryptoServiceAbstraction,
|
||||
collectionService: CollectionServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
policyService: PolicyServiceAbstraction,
|
||||
sendService: SendServiceAbstraction,
|
||||
logService: LogService,
|
||||
keyConnectorService: KeyConnectorServiceAbstraction,
|
||||
stateService: StateServiceAbstraction,
|
||||
organizationService: OrganizationServiceAbstraction,
|
||||
providerService: ProviderServiceAbstraction,
|
||||
) => new SyncService(
|
||||
apiService,
|
||||
settingsService,
|
||||
folderService,
|
||||
cipherService,
|
||||
cryptoService,
|
||||
collectionService,
|
||||
messagingService,
|
||||
policyService,
|
||||
sendService,
|
||||
logService,
|
||||
keyConnectorService,
|
||||
stateService,
|
||||
organizationService,
|
||||
providerService,
|
||||
async (expired: boolean) => messagingService.send('logout', { expired: expired })),
|
||||
deps: [
|
||||
UserServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
SettingsServiceAbstraction,
|
||||
FolderServiceAbstraction,
|
||||
CipherServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
CollectionServiceAbstraction,
|
||||
StorageServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
PolicyServiceAbstraction,
|
||||
SendServiceAbstraction,
|
||||
LogService,
|
||||
TokenServiceAbstraction,
|
||||
KeyConnectorServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
OrganizationServiceAbstraction,
|
||||
ProviderServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: UserServiceAbstraction,
|
||||
useClass: UserService,
|
||||
deps: [TokenServiceAbstraction, StorageServiceAbstraction],
|
||||
},
|
||||
{ provide: BroadcasterServiceAbstraction, useClass: BroadcasterService },
|
||||
{
|
||||
provide: SettingsServiceAbstraction,
|
||||
useClass: SettingsService,
|
||||
deps: [UserServiceAbstraction, StorageServiceAbstraction],
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: VaultTimeoutServiceAbstraction,
|
||||
useFactory: (cipherService: CipherServiceAbstraction, folderService: FolderServiceAbstraction,
|
||||
collectionService: CollectionServiceAbstraction, cryptoService: CryptoServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction, storageService: StorageServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction, searchService: SearchServiceAbstraction,
|
||||
userService: UserServiceAbstraction, tokenService: TokenServiceAbstraction,
|
||||
policyService: PolicyServiceAbstraction, keyConnectorService: KeyConnectorServiceAbstraction) =>
|
||||
new VaultTimeoutService(cipherService, folderService, collectionService, cryptoService,
|
||||
platformUtilsService, storageService, messagingService, searchService, userService, tokenService,
|
||||
policyService, keyConnectorService, null,
|
||||
async () => messagingService.send('logout', { expired: false })),
|
||||
useFactory: (
|
||||
cipherService: CipherServiceAbstraction,
|
||||
folderService: FolderServiceAbstraction,
|
||||
collectionService: CollectionServiceAbstraction,
|
||||
cryptoService: CryptoServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
searchService: SearchServiceAbstraction,
|
||||
tokenService: TokenServiceAbstraction,
|
||||
policyService: PolicyServiceAbstraction,
|
||||
keyConnectorService: KeyConnectorServiceAbstraction,
|
||||
stateService: StateServiceAbstraction,
|
||||
) => new VaultTimeoutService(
|
||||
cipherService,
|
||||
folderService,
|
||||
collectionService,
|
||||
cryptoService,
|
||||
platformUtilsService,
|
||||
messagingService,
|
||||
searchService,
|
||||
tokenService,
|
||||
policyService,
|
||||
keyConnectorService,
|
||||
stateService,
|
||||
null,
|
||||
async () => messagingService.send('logout', { expired: false }),
|
||||
),
|
||||
deps: [
|
||||
CipherServiceAbstraction,
|
||||
FolderServiceAbstraction,
|
||||
CollectionServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
StorageServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
SearchServiceAbstraction,
|
||||
UserServiceAbstraction,
|
||||
TokenServiceAbstraction,
|
||||
PolicyServiceAbstraction,
|
||||
KeyConnectorServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: StateServiceAbstraction,
|
||||
useClass: StateService,
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
'SECURE_STORAGE',
|
||||
LogService,
|
||||
StateMigrationServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: StateMigrationServiceAbstraction,
|
||||
useClass: StateMigrationService,
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
'SECURE_STORAGE',
|
||||
],
|
||||
},
|
||||
{ provide: StateServiceAbstraction, useClass: StateService },
|
||||
{
|
||||
provide: ExportServiceAbstraction,
|
||||
useClass: ExportService,
|
||||
@@ -308,14 +371,26 @@ import { ValidationService } from './validation.service';
|
||||
},
|
||||
{
|
||||
provide: NotificationsServiceAbstraction,
|
||||
useFactory: (userService: UserServiceAbstraction, syncService: SyncServiceAbstraction,
|
||||
appIdService: AppIdServiceAbstraction, apiService: ApiServiceAbstraction,
|
||||
vaultTimeoutService: VaultTimeoutServiceAbstraction, environmentService: EnvironmentServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction, logService: LogService) =>
|
||||
new NotificationsService(userService, syncService, appIdService, apiService, vaultTimeoutService,
|
||||
environmentService, async () => messagingService.send('logout', { expired: true }), logService),
|
||||
useFactory: (
|
||||
syncService: SyncServiceAbstraction,
|
||||
appIdService: AppIdServiceAbstraction,
|
||||
apiService: ApiServiceAbstraction,
|
||||
vaultTimeoutService: VaultTimeoutServiceAbstraction,
|
||||
environmentService: EnvironmentServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
logService: LogService,
|
||||
stateService: StateServiceAbstraction,
|
||||
) => new NotificationsService(
|
||||
syncService,
|
||||
appIdService,
|
||||
apiService,
|
||||
vaultTimeoutService,
|
||||
environmentService,
|
||||
async () => messagingService.send('logout', { expired: true }),
|
||||
logService,
|
||||
stateService,
|
||||
),
|
||||
deps: [
|
||||
UserServiceAbstraction,
|
||||
SyncServiceAbstraction,
|
||||
AppIdServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
@@ -323,6 +398,7 @@ import { ValidationService } from './validation.service';
|
||||
EnvironmentServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -334,19 +410,19 @@ import { ValidationService } from './validation.service';
|
||||
provide: EventServiceAbstraction,
|
||||
useClass: EventService,
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
UserServiceAbstraction,
|
||||
CipherServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
LogService,
|
||||
OrganizationServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: PolicyServiceAbstraction,
|
||||
useClass: PolicyService,
|
||||
deps: [
|
||||
UserServiceAbstraction,
|
||||
StorageServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
OrganizationServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
],
|
||||
},
|
||||
@@ -355,24 +431,23 @@ import { ValidationService } from './validation.service';
|
||||
useClass: SendService,
|
||||
deps: [
|
||||
CryptoServiceAbstraction,
|
||||
UserServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
FileUploadServiceAbstraction,
|
||||
StorageServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
CryptoFunctionServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: KeyConnectorServiceAbstraction,
|
||||
useClass: KeyConnectorService,
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
UserServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
TokenServiceAbstraction,
|
||||
LogService,
|
||||
OrganizationServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -385,6 +460,20 @@ import { ValidationService } from './validation.service';
|
||||
],
|
||||
},
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
{
|
||||
provide: OrganizationServiceAbstraction,
|
||||
useClass: OrganizationService,
|
||||
deps: [
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: ProviderServiceAbstraction,
|
||||
useClass: ProviderService,
|
||||
deps: [
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class JslibServicesModule {
|
||||
|
||||
@@ -4,29 +4,26 @@ import {
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
|
||||
@Injectable()
|
||||
export class LockGuardService implements CanActivate {
|
||||
|
||||
protected homepage = 'vault';
|
||||
constructor(private vaultTimeoutService: VaultTimeoutService, private userService: UserService,
|
||||
private router: Router) { }
|
||||
constructor(private vaultTimeoutService: VaultTimeoutService, private router: Router,
|
||||
private stateService: StateService) { }
|
||||
|
||||
async canActivate() {
|
||||
const isAuthed = await this.userService.isAuthenticated();
|
||||
if (isAuthed) {
|
||||
const locked = await this.vaultTimeoutService.isLocked();
|
||||
if (locked) {
|
||||
return true;
|
||||
} else {
|
||||
this.router.navigate([this.homepage]);
|
||||
return false;
|
||||
}
|
||||
if (!await this.stateService.getIsAuthenticated()) {
|
||||
this.router.navigate(['login']);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.router.navigate(['']);
|
||||
return false;
|
||||
if (!await this.vaultTimeoutService.isLocked()) {
|
||||
this.router.navigate([this.homepage]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
|
||||
@Injectable()
|
||||
export class UnauthGuardService implements CanActivate {
|
||||
|
||||
protected homepage = 'vault';
|
||||
constructor(private vaultTimeoutService: VaultTimeoutService, private userService: UserService,
|
||||
private router: Router) { }
|
||||
constructor(private vaultTimeoutService: VaultTimeoutService, private router: Router,
|
||||
private stateService: StateService) { }
|
||||
|
||||
async canActivate() {
|
||||
const isAuthed = await this.userService.isAuthenticated();
|
||||
const isAuthed = await this.stateService.getIsAuthenticated();
|
||||
if (isAuthed) {
|
||||
const locked = await this.vaultTimeoutService.isLocked();
|
||||
if (locked) {
|
||||
@@ -26,7 +25,6 @@ export class UnauthGuardService implements CanActivate {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
14
common/package-lock.json
generated
14
common/package-lock.json
generated
@@ -24,7 +24,7 @@
|
||||
"@fluffy-spoon/substitute": "^1.208.0",
|
||||
"@types/jasmine": "^3.10.2",
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/node": "^14.17.1",
|
||||
"@types/node": "^16.11.12",
|
||||
"@types/node-forge": "^0.9.7",
|
||||
"@types/papaparse": "^5.2.5",
|
||||
"@types/tldjs": "^2.3.0",
|
||||
@@ -213,9 +213,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "14.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.0.tgz",
|
||||
"integrity": "sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ==",
|
||||
"version": "16.11.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
|
||||
"integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node-forge": {
|
||||
@@ -4362,9 +4362,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.0.tgz",
|
||||
"integrity": "sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ==",
|
||||
"version": "16.11.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
|
||||
"integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node-forge": {
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"@fluffy-spoon/substitute": "^1.208.0",
|
||||
"@types/jasmine": "^3.10.2",
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/node": "^14.17.1",
|
||||
"@types/node": "^16.11.12",
|
||||
"@types/node-forge": "^0.9.7",
|
||||
"@types/papaparse": "^5.2.5",
|
||||
"@types/tldjs": "^2.3.0",
|
||||
|
||||
80
common/spec/importers/fsecureFskImporter.spec.ts
Normal file
80
common/spec/importers/fsecureFskImporter.spec.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { FSecureFskImporter as Importer } from 'src/importers/fsecureFskImporter';
|
||||
|
||||
const TestDataWithStyleSetToWebsite: string =
|
||||
JSON.stringify({
|
||||
data: {
|
||||
'8d58b5cf252dd06fbd98f5289e918ab1': {
|
||||
color: '#00baff',
|
||||
reatedDate: 1609302913,
|
||||
creditCvv: '',
|
||||
creditExpiry: '',
|
||||
creditNumber: '',
|
||||
favorite: 0,
|
||||
modifiedDate: 1609302913,
|
||||
notes: 'note',
|
||||
password: 'word',
|
||||
passwordList: [],
|
||||
passwordModifiedDate: 1609302913,
|
||||
rev: 1,
|
||||
service: 'My first pass',
|
||||
style: 'website',
|
||||
type: 1,
|
||||
url: 'https://bitwarden.com',
|
||||
username: 'pass',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const TestDataWithStyleSetToGlobe: string =
|
||||
JSON.stringify({
|
||||
data: {
|
||||
'8d58b5cf252dd06fbd98f5289e918ab1': {
|
||||
color: '#00baff',
|
||||
reatedDate: 1609302913,
|
||||
creditCvv: '',
|
||||
creditExpiry: '',
|
||||
creditNumber: '',
|
||||
favorite: 0,
|
||||
modifiedDate: 1609302913,
|
||||
notes: 'note',
|
||||
password: 'word',
|
||||
passwordList: [],
|
||||
passwordModifiedDate: 1609302913,
|
||||
rev: 1,
|
||||
service: 'My first pass',
|
||||
style: 'globe',
|
||||
type: 1,
|
||||
url: 'https://bitwarden.com',
|
||||
username: 'pass',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
describe('FSecure FSK Importer', () => {
|
||||
it('should parse data with style set to website', async () => {
|
||||
const importer = new Importer();
|
||||
const result = await importer.parse(TestDataWithStyleSetToWebsite);
|
||||
expect(result != null).toBe(true);
|
||||
|
||||
const cipher = result.ciphers.shift();
|
||||
expect(cipher.login.username).toEqual('pass');
|
||||
expect(cipher.login.password).toEqual('word');
|
||||
expect(cipher.login.uris.length).toEqual(1);
|
||||
const uriView = cipher.login.uris.shift();
|
||||
expect(uriView.uri).toEqual('https://bitwarden.com');
|
||||
});
|
||||
|
||||
it('should parse data with style set to globe', async () => {
|
||||
const importer = new Importer();
|
||||
const result = await importer.parse(TestDataWithStyleSetToGlobe);
|
||||
expect(result != null).toBe(true);
|
||||
|
||||
const cipher = result.ciphers.shift();
|
||||
expect(cipher.login.username).toEqual('pass');
|
||||
expect(cipher.login.password).toEqual('word');
|
||||
expect(cipher.login.uris.length).toEqual(1);
|
||||
const uriView = cipher.login.uris.shift();
|
||||
expect(uriView.uri).toEqual('https://bitwarden.com');
|
||||
});
|
||||
});
|
||||
@@ -7,8 +7,8 @@ import { I18nService } from 'src/abstractions/i18n.service';
|
||||
import { LogService } from 'src/abstractions/log.service';
|
||||
import { SearchService } from 'src/abstractions/search.service';
|
||||
import { SettingsService } from 'src/abstractions/settings.service';
|
||||
import { StorageService } from 'src/abstractions/storage.service';
|
||||
import { UserService } from 'src/abstractions/user.service';
|
||||
import { StateService } from 'src/abstractions/state.service';
|
||||
|
||||
import { Utils } from 'src/misc/utils';
|
||||
import { Cipher } from 'src/models/domain/cipher';
|
||||
import { EncArrayBuffer } from 'src/models/domain/encArrayBuffer';
|
||||
@@ -22,11 +22,10 @@ const ENCRYPTED_BYTES = new EncArrayBuffer(Utils.fromUtf8ToArray(ENCRYPTED_TEXT)
|
||||
|
||||
describe('Cipher Service', () => {
|
||||
let cryptoService: SubstituteOf<CryptoService>;
|
||||
let userService: SubstituteOf<UserService>;
|
||||
let stateService: SubstituteOf<StateService>;
|
||||
let settingsService: SubstituteOf<SettingsService>;
|
||||
let apiService: SubstituteOf<ApiService>;
|
||||
let fileUploadService: SubstituteOf<FileUploadService>;
|
||||
let storageService: SubstituteOf<StorageService>;
|
||||
let i18nService: SubstituteOf<I18nService>;
|
||||
let searchService: SubstituteOf<SearchService>;
|
||||
let logService: SubstituteOf<LogService>;
|
||||
@@ -35,11 +34,10 @@ describe('Cipher Service', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
cryptoService = Substitute.for<CryptoService>();
|
||||
userService = Substitute.for<UserService>();
|
||||
stateService = Substitute.for<StateService>();
|
||||
settingsService = Substitute.for<SettingsService>();
|
||||
apiService = Substitute.for<ApiService>();
|
||||
fileUploadService = Substitute.for<FileUploadService>();
|
||||
storageService = Substitute.for<StorageService>();
|
||||
i18nService = Substitute.for<I18nService>();
|
||||
searchService = Substitute.for<SearchService>();
|
||||
logService = Substitute.for<LogService>();
|
||||
@@ -47,12 +45,11 @@ describe('Cipher Service', () => {
|
||||
cryptoService.encryptToBytes(Arg.any(), Arg.any()).resolves(ENCRYPTED_BYTES);
|
||||
cryptoService.encrypt(Arg.any(), Arg.any()).resolves(new EncString(ENCRYPTED_TEXT));
|
||||
|
||||
cipherService = new CipherService(cryptoService, userService, settingsService, apiService, fileUploadService,
|
||||
storageService, i18nService, () => searchService, logService);
|
||||
cipherService = new CipherService(cryptoService, settingsService, apiService, fileUploadService,
|
||||
i18nService, () => searchService, logService, stateService);
|
||||
});
|
||||
|
||||
it('attachments upload encrypted file contents', async () => {
|
||||
const key = new SymmetricCryptoKey(new Uint8Array(32).buffer);
|
||||
const fileName = 'filename';
|
||||
const fileData = new Uint8Array(10).buffer;
|
||||
cryptoService.getOrgKey(Arg.any()).resolves(new SymmetricCryptoKey(new Uint8Array(32).buffer));
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
export abstract class ApiKeyService {
|
||||
setInformation: (clientId: string, clientSecret: string) => Promise<any>;
|
||||
clear: () => Promise<any>;
|
||||
getClientId: () => Promise<string>;
|
||||
getClientSecret: () => Promise<string>;
|
||||
getEntityType: () => Promise<string>;
|
||||
getEntityId: () => Promise<string>;
|
||||
isAuthenticated: () => Promise<boolean>;
|
||||
}
|
||||
@@ -11,9 +11,7 @@ import { CipherView } from '../models/view/cipherView';
|
||||
import { FieldView } from '../models/view/fieldView';
|
||||
|
||||
export abstract class CipherService {
|
||||
decryptedCipherCache: CipherView[];
|
||||
|
||||
clearCache: () => void;
|
||||
clearCache: (userId?: string) => Promise<void>;
|
||||
encrypt: (model: CipherView, key?: SymmetricCryptoKey, originalCipher?: Cipher) => Promise<Cipher>;
|
||||
encryptFields: (fieldsModel: FieldView[], key: SymmetricCryptoKey) => Promise<Field[]>;
|
||||
encryptField: (fieldModel: FieldView, key: SymmetricCryptoKey) => Promise<Field>;
|
||||
|
||||
@@ -6,9 +6,7 @@ import { TreeNode } from '../models/domain/treeNode';
|
||||
import { CollectionView } from '../models/view/collectionView';
|
||||
|
||||
export abstract class CollectionService {
|
||||
decryptedCollectionCache: CollectionView[];
|
||||
|
||||
clearCache: () => void;
|
||||
clearCache: (userId?: string) => Promise<void>;
|
||||
encrypt: (model: CollectionView) => Promise<Collection>;
|
||||
decryptMany: (collections: Collection[]) => Promise<CollectionView[]>;
|
||||
get: (id: string) => Promise<Collection>;
|
||||
|
||||
@@ -8,17 +8,16 @@ import { ProfileProviderResponse } from '../models/response/profileProviderRespo
|
||||
|
||||
import { HashPurpose } from '../enums/hashPurpose';
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
|
||||
import { KeySuffixOptions } from './storage.service';
|
||||
import { KeySuffixOptions } from '../enums/keySuffixOptions';
|
||||
|
||||
export abstract class CryptoService {
|
||||
setKey: (key: SymmetricCryptoKey) => Promise<any>;
|
||||
setKeyHash: (keyHash: string) => Promise<{}>;
|
||||
setEncKey: (encKey: string) => Promise<{}>;
|
||||
setEncPrivateKey: (encPrivateKey: string) => Promise<{}>;
|
||||
setOrgKeys: (orgs: ProfileOrganizationResponse[], providerOrgs: ProfileProviderOrganizationResponse[]) => Promise<{}>;
|
||||
setProviderKeys: (orgs: ProfileProviderResponse[]) => Promise<{}>;
|
||||
getKey: (keySuffix?: KeySuffixOptions) => Promise<SymmetricCryptoKey>;
|
||||
setKeyHash: (keyHash: string) => Promise<void>;
|
||||
setEncKey: (encKey: string) => Promise<void>;
|
||||
setEncPrivateKey: (encPrivateKey: string) => Promise<void>;
|
||||
setOrgKeys: (orgs: ProfileOrganizationResponse[], providerOrgs: ProfileProviderOrganizationResponse[]) => Promise<void>;
|
||||
setProviderKeys: (orgs: ProfileProviderResponse[]) => Promise<void>;
|
||||
getKey: (keySuffix?: KeySuffixOptions, userId?: string) => Promise<SymmetricCryptoKey>;
|
||||
getKeyFromStorage: (keySuffix: KeySuffixOptions) => Promise<SymmetricCryptoKey>;
|
||||
getKeyHash: () => Promise<string>;
|
||||
compareAndUpdateKeyHash: (masterPassword: string, key: SymmetricCryptoKey) => Promise<boolean>;
|
||||
@@ -30,17 +29,17 @@ export abstract class CryptoService {
|
||||
getOrgKey: (orgId: string) => Promise<SymmetricCryptoKey>;
|
||||
getProviderKey: (providerId: string) => Promise<SymmetricCryptoKey>;
|
||||
hasKey: () => Promise<boolean>;
|
||||
hasKeyInMemory: () => boolean;
|
||||
hasKeyStored: (keySuffix?: KeySuffixOptions) => Promise<boolean>;
|
||||
hasKeyInMemory: (userId?: string) => Promise<boolean>;
|
||||
hasKeyStored: (keySuffix?: KeySuffixOptions, userId?: string) => Promise<boolean>;
|
||||
hasEncKey: () => Promise<boolean>;
|
||||
clearKey: (clearSecretStorage?: boolean) => Promise<any>;
|
||||
clearKey: (clearSecretStorage?: boolean, userId?: string) => Promise<any>;
|
||||
clearKeyHash: () => Promise<any>;
|
||||
clearEncKey: (memoryOnly?: boolean) => Promise<any>;
|
||||
clearKeyPair: (memoryOnly?: boolean) => Promise<any>;
|
||||
clearOrgKeys: (memoryOnly?: boolean) => Promise<any>;
|
||||
clearEncKey: (memoryOnly?: boolean, userId?: string) => Promise<any>;
|
||||
clearKeyPair: (memoryOnly?: boolean, userId?: string) => Promise<any>;
|
||||
clearOrgKeys: (memoryOnly?: boolean, userId?: string) => Promise<any>;
|
||||
clearProviderKeys: (memoryOnly?: boolean) => Promise<any>;
|
||||
clearPinProtectedKey: () => Promise<any>;
|
||||
clearKeys: () => Promise<any>;
|
||||
clearKeys: (userId?: string) => Promise<any>;
|
||||
toggleKey: () => Promise<any>;
|
||||
makeKey: (password: string, salt: string, kdf: KdfType, kdfIterations: number) => Promise<SymmetricCryptoKey>;
|
||||
makeKeyFromPin: (pin: string, salt: string, kdf: KdfType, kdfIterations: number,
|
||||
|
||||
@@ -2,6 +2,6 @@ import { EventType } from '../enums/eventType';
|
||||
|
||||
export abstract class EventService {
|
||||
collect: (eventType: EventType, cipherId?: string, uploadImmediately?: boolean) => Promise<any>;
|
||||
uploadEvents: () => Promise<any>;
|
||||
clearEvents: () => Promise<any>;
|
||||
uploadEvents: (userId?: string) => Promise<any>;
|
||||
clearEvents: (userId?: string) => Promise<any>;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ import { TreeNode } from '../models/domain/treeNode';
|
||||
import { FolderView } from '../models/view/folderView';
|
||||
|
||||
export abstract class FolderService {
|
||||
decryptedFolderCache: FolderView[];
|
||||
|
||||
clearCache: () => void;
|
||||
clearCache: (userId?: string) => Promise<void>;
|
||||
encrypt: (model: FolderView, key?: SymmetricCryptoKey) => Promise<Folder>;
|
||||
get: (id: string) => Promise<Folder>;
|
||||
getAll: () => Promise<Folder[]>;
|
||||
|
||||
11
common/src/abstractions/organization.service.ts
Normal file
11
common/src/abstractions/organization.service.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { OrganizationData } from '../models/data/organizationData';
|
||||
|
||||
import { Organization } from '../models/domain/organization';
|
||||
|
||||
export abstract class OrganizationService {
|
||||
get: (id: string) => Promise<Organization>;
|
||||
getByIdentifier: (identifier: string) => Promise<Organization>;
|
||||
getAll: (userId?: string) => Promise<Organization[]>;
|
||||
save: (orgs: {[id: string]: OrganizationData}) => Promise<any>;
|
||||
canManageSponsorships: () => Promise<boolean>;
|
||||
}
|
||||
@@ -12,7 +12,7 @@ export abstract class PasswordGenerationService {
|
||||
saveOptions: (options: any) => Promise<any>;
|
||||
getHistory: () => Promise<GeneratedPasswordHistory[]>;
|
||||
addHistory: (password: string) => Promise<any>;
|
||||
clear: () => Promise<any>;
|
||||
clear: (userId?: string) => Promise<any>;
|
||||
passwordStrength: (password: string, userInputs?: string[]) => zxcvbn.ZXCVBNResult;
|
||||
normalizeOptions: (options: any, enforcedPolicyOptions: PasswordGeneratorPolicyOptions) => void;
|
||||
}
|
||||
|
||||
@@ -10,17 +10,15 @@ import { PolicyResponse } from '../models/response/policyResponse';
|
||||
import { PolicyType } from '../enums/policyType';
|
||||
|
||||
export abstract class PolicyService {
|
||||
policyCache: Policy[];
|
||||
|
||||
clearCache: () => void;
|
||||
getAll: (type?: PolicyType) => Promise<Policy[]>;
|
||||
getAll: (type?: PolicyType, userId?: string) => Promise<Policy[]>;
|
||||
getPolicyForOrganization: (policyType: PolicyType, organizationId: string) => Promise<Policy>;
|
||||
replace: (policies: { [id: string]: PolicyData; }) => Promise<any>;
|
||||
clear: (userId: string) => Promise<any>;
|
||||
clear: (userId?: string) => Promise<any>;
|
||||
getMasterPasswordPolicyOptions: (policies?: Policy[]) => Promise<MasterPasswordPolicyOptions>;
|
||||
evaluateMasterPassword: (passwordStrength: number, newPassword: string,
|
||||
enforcedPolicyOptions?: MasterPasswordPolicyOptions) => boolean;
|
||||
getResetPasswordPolicyOptions: (policies: Policy[], orgId: string) => [ResetPasswordPolicyOptions, boolean];
|
||||
mapPoliciesFromToken: (policiesResponse: ListResponse<PolicyResponse>) => Policy[];
|
||||
policyAppliesToUser: (policyType: PolicyType, policyFilter?: (policy: Policy) => boolean) => Promise<boolean>;
|
||||
policyAppliesToUser: (policyType: PolicyType, policyFilter?: (policy: Policy) => boolean, userId?: string) => Promise<boolean>;
|
||||
}
|
||||
|
||||
9
common/src/abstractions/provider.service.ts
Normal file
9
common/src/abstractions/provider.service.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { ProviderData } from '../models/data/providerData';
|
||||
|
||||
import { Provider } from '../models/domain/provider';
|
||||
|
||||
export abstract class ProviderService {
|
||||
get: (id: string) => Promise<Provider>;
|
||||
getAll: () => Promise<Provider[]>;
|
||||
save: (providers: {[id: string]: ProviderData}) => Promise<any>;
|
||||
}
|
||||
@@ -7,9 +7,7 @@ import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
import { SendView } from '../models/view/sendView';
|
||||
|
||||
export abstract class SendService {
|
||||
decryptedSendCache: SendView[];
|
||||
|
||||
clearCache: () => void;
|
||||
clearCache: () => Promise<void>;
|
||||
encrypt: (model: SendView, file: File | ArrayBuffer, password: string, key?: SymmetricCryptoKey) => Promise<[Send, EncArrayBuffer]>;
|
||||
get: (id: string) => Promise<Send>;
|
||||
getAll: () => Promise<Send[]>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export abstract class SettingsService {
|
||||
clearCache: () => void;
|
||||
clearCache: () => Promise<void>;
|
||||
getEquivalentDomains: () => Promise<any>;
|
||||
setEquivalentDomains: (equivalentDomains: string[][]) => Promise<any>;
|
||||
clear: (userId: string) => Promise<void>;
|
||||
clear: (userId?: string) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,259 @@
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
import { UriMatchType } from '../enums/uriMatchType';
|
||||
|
||||
import { CipherData } from '../models/data/cipherData';
|
||||
import { CollectionData } from '../models/data/collectionData';
|
||||
import { EventData } from '../models/data/eventData';
|
||||
import { FolderData } from '../models/data/folderData';
|
||||
import { OrganizationData } from '../models/data/organizationData';
|
||||
import { PolicyData } from '../models/data/policyData';
|
||||
import { ProviderData } from '../models/data/providerData';
|
||||
import { SendData } from '../models/data/sendData';
|
||||
|
||||
import { Account } from '../models/domain/account';
|
||||
import { EncString } from '../models/domain/encString';
|
||||
import { GeneratedPasswordHistory } from '../models/domain/generatedPasswordHistory';
|
||||
import { Policy } from '../models/domain/policy';
|
||||
import { StorageOptions } from '../models/domain/storageOptions';
|
||||
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
|
||||
import { CipherView } from '../models/view/cipherView';
|
||||
import { CollectionView } from '../models/view/collectionView';
|
||||
import { FolderView } from '../models/view/folderView';
|
||||
import { SendView } from '../models/view/sendView';
|
||||
|
||||
export abstract class StateService {
|
||||
get: <T>(key: string) => Promise<T>;
|
||||
save: (key: string, obj: any) => Promise<any>;
|
||||
remove: (key: string) => Promise<any>;
|
||||
purge: () => Promise<any>;
|
||||
accounts: BehaviorSubject<{ [userId: string]: Account }>;
|
||||
activeAccount: BehaviorSubject<string>;
|
||||
|
||||
addAccount: (account: Account) => Promise<void>;
|
||||
setActiveUser: (userId: string) => Promise<void>;
|
||||
clean: (options?: StorageOptions) => Promise<void>;
|
||||
init: () => Promise<void>;
|
||||
|
||||
getAccessToken: (options?: StorageOptions) => Promise<string>;
|
||||
setAccessToken: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getAddEditCipherInfo: (options?: StorageOptions) => Promise<any>;
|
||||
setAddEditCipherInfo: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getAlwaysShowDock: (options?: StorageOptions) => Promise<boolean>;
|
||||
setAlwaysShowDock: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getApiKeyClientId: (options?: StorageOptions) => Promise<string>;
|
||||
setApiKeyClientId: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getApiKeyClientSecret: (options?: StorageOptions) => Promise<string>;
|
||||
setApiKeyClientSecret: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getAutoConfirmFingerPrints: (options?: StorageOptions) => Promise<boolean>;
|
||||
setAutoConfirmFingerprints: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getAutoFillOnPageLoadDefault: (options?: StorageOptions) => Promise<boolean>;
|
||||
setAutoFillOnPageLoadDefault: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricAwaitingAcceptance: (options?: StorageOptions) => Promise<boolean>;
|
||||
setBiometricAwaitingAcceptance: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricFingerprintValidated: (options?: StorageOptions) => Promise<boolean>;
|
||||
setBiometricFingerprintValidated: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricLocked: (options?: StorageOptions) => Promise<boolean>;
|
||||
setBiometricLocked: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricText: (options?: StorageOptions) => Promise<string>;
|
||||
setBiometricText: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricUnlock: (options?: StorageOptions) => Promise<boolean>;
|
||||
setBiometricUnlock: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getCanAccessPremium: (options?: StorageOptions) => Promise<boolean>;
|
||||
getClearClipboard: (options?: StorageOptions) => Promise<number>;
|
||||
setClearClipboard: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getCollapsedGroupings: (options?: StorageOptions) => Promise<Set<string>>;
|
||||
setCollapsedGroupings: (value: Set<string>, options?: StorageOptions) => Promise<void>;
|
||||
getConvertAccountToKeyConnector: (options?: StorageOptions) => Promise<boolean>;
|
||||
setConvertAccountToKeyConnector: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getCryptoMasterKey: (options?: StorageOptions) => Promise<SymmetricCryptoKey>;
|
||||
setCryptoMasterKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise<void>;
|
||||
getCryptoMasterKeyAuto: (options?: StorageOptions) => Promise<string>;
|
||||
setCryptoMasterKeyAuto: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getCryptoMasterKeyB64: (options?: StorageOptions) => Promise<string>;
|
||||
setCryptoMasterKeyB64: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<string>;
|
||||
hasCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<boolean>;
|
||||
setCryptoMasterKeyBiometric: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getDecodedToken: (options?: StorageOptions) => Promise<any>;
|
||||
setDecodedToken: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedCiphers: (options?: StorageOptions) => Promise<CipherView[]>;
|
||||
setDecryptedCiphers: (value: CipherView[], options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedCollections: (options?: StorageOptions) => Promise<CollectionView[]>;
|
||||
setDecryptedCollections: (value: CollectionView[], options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedCryptoSymmetricKey: (options?: StorageOptions) => Promise<SymmetricCryptoKey>;
|
||||
setDecryptedCryptoSymmetricKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedFolders: (options?: StorageOptions) => Promise<FolderView[]>;
|
||||
setDecryptedFolders: (value: FolderView[], options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedOrganizationKeys: (options?: StorageOptions) => Promise<Map<string, SymmetricCryptoKey>>;
|
||||
setDecryptedOrganizationKeys: (value: Map<string, SymmetricCryptoKey>, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedPasswordGenerationHistory: (options?: StorageOptions) => Promise<GeneratedPasswordHistory[]>;
|
||||
setDecryptedPasswordGenerationHistory: (value: GeneratedPasswordHistory[], options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedPinProtected: (options?: StorageOptions) => Promise<EncString>;
|
||||
setDecryptedPinProtected: (value: EncString, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedPolicies: (options?: StorageOptions) => Promise<Policy[]>;
|
||||
setDecryptedPolicies: (value: Policy[], options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedPrivateKey: (options?: StorageOptions) => Promise<ArrayBuffer>;
|
||||
setDecryptedPrivateKey: (value: ArrayBuffer, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedProviderKeys: (options?: StorageOptions) => Promise<Map<string, SymmetricCryptoKey>>;
|
||||
setDecryptedProviderKeys: (value: Map<string, SymmetricCryptoKey>, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedSends: (options?: StorageOptions) => Promise<SendView[]>;
|
||||
setDecryptedSends: (value: SendView[], options?: StorageOptions) => Promise<void>;
|
||||
getDefaultUriMatch: (options?: StorageOptions) => Promise<UriMatchType>;
|
||||
setDefaultUriMatch: (value: UriMatchType, options?: StorageOptions) => Promise<void>;
|
||||
getDisableAddLoginNotification: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableAddLoginNotification: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableAutoBiometricsPrompt: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableAutoBiometricsPrompt: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableAutoTotpCopy: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableAutoTotpCopy: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableBadgeCounter: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableBadgeCounter: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableChangedPasswordNotification: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableChangedPasswordNotification: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableContextMenuItem: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableContextMenuItem: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableFavicon: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableFavicon: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableGa: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableGa: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDontShowCardsCurrentTab: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDontShowCardsCurrentTab: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDontShowIdentitiesCurrentTab: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDontShowIdentitiesCurrentTab: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEmail: (options?: StorageOptions) => Promise<string>;
|
||||
setEmail: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEmailVerified: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEmailVerified: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableAlwaysOnTop: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableAlwaysOnTop: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableAutoFillOnPageLoad: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableAutoFillOnPageLoad: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableBiometric: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableBiometric: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableBrowserIntegration: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableBrowserIntegration: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableBrowserIntegrationFingerprint: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableBrowserIntegrationFingerprint: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableCloseToTray: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableCloseToTray: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableFullWidth: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableFullWidth: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableGravitars: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableGravitars: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableMinimizeToTray: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableMinimizeToTray: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableStartToTray: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableStartToTray: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableTray: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableTray: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedCiphers: (options?: StorageOptions) => Promise<{ [id: string]: CipherData }>;
|
||||
setEncryptedCiphers: (value: { [id: string]: CipherData }, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedCollections: (options?: StorageOptions) => Promise<{ [id: string]: CollectionData }>;
|
||||
setEncryptedCollections: (value: { [id: string]: CollectionData }, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedCryptoSymmetricKey: (options?: StorageOptions) => Promise<string>;
|
||||
setEncryptedCryptoSymmetricKey: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedFolders: (options?: StorageOptions) => Promise<{ [id: string]: FolderData }>;
|
||||
setEncryptedFolders: (value: { [id: string]: FolderData }, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedOrganizationKeys: (options?: StorageOptions) => Promise<any>;
|
||||
setEncryptedOrganizationKeys: (value: Map<string, SymmetricCryptoKey>, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedPasswordGenerationHistory: (options?: StorageOptions) => Promise<GeneratedPasswordHistory[]>;
|
||||
setEncryptedPasswordGenerationHistory: (value: GeneratedPasswordHistory[], options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedPinProtected: (options?: StorageOptions) => Promise<string>;
|
||||
setEncryptedPinProtected: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedPolicies: (options?: StorageOptions) => Promise<{ [id: string]: PolicyData }>;
|
||||
setEncryptedPolicies: (value: { [id: string]: PolicyData }, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedPrivateKey: (options?: StorageOptions) => Promise<string>;
|
||||
setEncryptedPrivateKey: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedProviderKeys: (options?: StorageOptions) => Promise<any>;
|
||||
setEncryptedProviderKeys: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedSends: (options?: StorageOptions) => Promise<{ [id: string]: SendData }>;
|
||||
setEncryptedSends: (value: { [id: string]: SendData }, options?: StorageOptions) => Promise<void>;
|
||||
getEntityId: (options?: StorageOptions) => Promise<string>;
|
||||
setEntityId: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEntityType: (options?: StorageOptions) => Promise<any>;
|
||||
setEntityType: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEnvironmentUrls: (options?: StorageOptions) => Promise<any>;
|
||||
setEnvironmentUrls: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getEquivalentDomains: (options?: StorageOptions) => Promise<any>;
|
||||
setEquivalentDomains: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEventCollection: (options?: StorageOptions) => Promise<EventData[]>;
|
||||
setEventCollection: (value: EventData[], options?: StorageOptions) => Promise<void>;
|
||||
getEverBeenUnlocked: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEverBeenUnlocked: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getForcePasswordReset: (options?: StorageOptions) => Promise<boolean>;
|
||||
setForcePasswordReset: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getInstalledVersion: (options?: StorageOptions) => Promise<string>;
|
||||
setInstalledVersion: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getIsAuthenticated: (options?: StorageOptions) => Promise<boolean>;
|
||||
getKdfIterations: (options?: StorageOptions) => Promise<number>;
|
||||
setKdfIterations: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getKdfType: (options?: StorageOptions) => Promise<KdfType>;
|
||||
setKdfType: (value: KdfType, options?: StorageOptions) => Promise<void>;
|
||||
getKeyHash: (options?: StorageOptions) => Promise<string>;
|
||||
setKeyHash: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getLastActive: (options?: StorageOptions) => Promise<number>;
|
||||
setLastActive: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getLastSync: (options?: StorageOptions) => Promise<string>;
|
||||
setLastSync: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getLegacyEtmKey: (options?: StorageOptions) => Promise<SymmetricCryptoKey>;
|
||||
setLegacyEtmKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise<void>;
|
||||
getLocalData: (options?: StorageOptions) => Promise<any>;
|
||||
setLocalData: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getLocale: (options?: StorageOptions) => Promise<string>;
|
||||
setLocale: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getLoginRedirect: (options?: StorageOptions) => Promise<any>;
|
||||
setLoginRedirect: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getMainWindowSize: (options?: StorageOptions) => Promise<number>;
|
||||
setMainWindowSize: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getMinimizeOnCopyToClipboard: (options?: StorageOptions) => Promise<boolean>;
|
||||
setMinimizeOnCopyToClipboard: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getNeverDomains: (options?: StorageOptions) => Promise<{ [id: string]: any }>;
|
||||
setNeverDomains: (value: { [id: string]: any }, options?: StorageOptions) => Promise<void>;
|
||||
getNoAutoPromptBiometrics: (options?: StorageOptions) => Promise<boolean>;
|
||||
setNoAutoPromptBiometrics: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getNoAutoPromptBiometricsText: (options?: StorageOptions) => Promise<string>;
|
||||
setNoAutoPromptBiometricsText: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getOpenAtLogin: (options?: StorageOptions) => Promise<boolean>;
|
||||
setOpenAtLogin: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getOrganizationInvitation: (options?: StorageOptions) => Promise<any>;
|
||||
setOrganizationInvitation: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getOrganizations: (options?: StorageOptions) => Promise<{ [id: string]: OrganizationData }>;
|
||||
setOrganizations: (value: { [id: string]: OrganizationData }, options?: StorageOptions) => Promise<void>;
|
||||
getPasswordGenerationOptions: (options?: StorageOptions) => Promise<any>;
|
||||
setPasswordGenerationOptions: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getProtectedPin: (options?: StorageOptions) => Promise<string>;
|
||||
setProtectedPin: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getProviders: (options?: StorageOptions) => Promise<{ [id: string]: ProviderData }>;
|
||||
setProviders: (value: { [id: string]: ProviderData }, options?: StorageOptions) => Promise<void>;
|
||||
getPublicKey: (options?: StorageOptions) => Promise<ArrayBuffer>;
|
||||
setPublicKey: (value: ArrayBuffer, options?: StorageOptions) => Promise<void>;
|
||||
getRefreshToken: (options?: StorageOptions) => Promise<string>;
|
||||
setRefreshToken: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getRememberedEmail: (options?: StorageOptions) => Promise<string>;
|
||||
setRememberedEmail: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSecurityStamp: (options?: StorageOptions) => Promise<string>;
|
||||
setSecurityStamp: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSettings: (options?: StorageOptions) => Promise<any>;
|
||||
setSettings: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSsoCodeVerifier: (options?: StorageOptions) => Promise<string>;
|
||||
setSsoCodeVerifier: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSsoOrgIdentifier: (options?: StorageOptions) => Promise<string>;
|
||||
setSsoOrganizationIdentifier: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSsoState: (options?: StorageOptions) => Promise<string>;
|
||||
setSsoState: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getTheme: (options?: StorageOptions) => Promise<string>;
|
||||
setTheme: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getTwoFactorToken: (options?: StorageOptions) => Promise<string>;
|
||||
setTwoFactorToken: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getUserId: (options?: StorageOptions) => Promise<string>;
|
||||
getUsesKeyConnector: (options?: StorageOptions) => Promise<boolean>;
|
||||
setUsesKeyConnector: (vaule: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getVaultTimeout: (options?: StorageOptions) => Promise<number>;
|
||||
setVaultTimeout: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getVaultTimeoutAction: (options?: StorageOptions) => Promise<string>;
|
||||
setVaultTimeoutAction: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getStateVersion: () => Promise<number>;
|
||||
setStateVersion: (value: number) => Promise<void>;
|
||||
getWindow: () => Promise<Map<string, any>>;
|
||||
setWindow: (value: Map<string, any>) => Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
4
common/src/abstractions/stateMigration.service.ts
Normal file
4
common/src/abstractions/stateMigration.service.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export abstract class StateMigrationService {
|
||||
needsMigration: () => Promise<boolean>;
|
||||
migrate: () => Promise<void>;
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
import { StorageOptions } from '../models/domain/storageOptions';
|
||||
|
||||
export abstract class StorageService {
|
||||
get: <T>(key: string, options?: StorageServiceOptions) => Promise<T>;
|
||||
has: (key: string, options?: StorageServiceOptions) => Promise<boolean>;
|
||||
save: (key: string, obj: any, options?: StorageServiceOptions) => Promise<any>;
|
||||
remove: (key: string, options?: StorageServiceOptions) => Promise<any>;
|
||||
get: <T>(key: string, options?: StorageOptions) => Promise<T>;
|
||||
has: (key: string, options?: StorageOptions) => Promise<boolean>;
|
||||
save: (key: string, obj: any, options?: StorageOptions) => Promise<any>;
|
||||
remove: (key: string, options?: StorageOptions) => Promise<any>;
|
||||
}
|
||||
|
||||
export interface StorageServiceOptions {
|
||||
keySuffix: KeySuffixOptions;
|
||||
}
|
||||
|
||||
export type KeySuffixOptions = 'auto' | 'biometric';
|
||||
|
||||
@@ -8,7 +8,7 @@ export abstract class SyncService {
|
||||
syncInProgress: boolean;
|
||||
|
||||
getLastSync: () => Promise<Date>;
|
||||
setLastSync: (date: Date) => Promise<any>;
|
||||
setLastSync: (date: Date, userId?: string) => Promise<any>;
|
||||
fullSync: (forceSync: boolean, allowThrowOnError?: boolean) => Promise<boolean>;
|
||||
syncUpsertFolder: (notification: SyncFolderNotification, isEdit: boolean) => Promise<boolean>;
|
||||
syncDeleteFolder: (notification: SyncFolderNotification) => Promise<boolean>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export abstract class SystemService {
|
||||
startProcessReload: () => void;
|
||||
startProcessReload: () => Promise<void>;
|
||||
cancelProcessReload: () => void;
|
||||
clearClipboard: (clipboardValue: string, timeoutMs?: number) => void;
|
||||
clearClipboard: (clipboardValue: string, timeoutMs?: number) => Promise<void>;
|
||||
clearPendingClipboard: () => Promise<any>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
export abstract class TokenService {
|
||||
token: string;
|
||||
decodedToken: any;
|
||||
refreshToken: string;
|
||||
setTokens: (accessToken: string, refreshToken: string, clientIdClientSecret: [string, string]) => Promise<any>;
|
||||
setToken: (token: string) => Promise<any>;
|
||||
getToken: () => Promise<string>;
|
||||
@@ -15,16 +12,16 @@ export abstract class TokenService {
|
||||
setTwoFactorToken: (token: string, email: string) => Promise<any>;
|
||||
getTwoFactorToken: (email: string) => Promise<string>;
|
||||
clearTwoFactorToken: (email: string) => Promise<any>;
|
||||
clearToken: () => Promise<any>;
|
||||
decodeToken: () => any;
|
||||
getTokenExpirationDate: () => Date;
|
||||
tokenSecondsRemaining: (offsetSeconds?: number) => number;
|
||||
tokenNeedsRefresh: (minutes?: number) => boolean;
|
||||
getUserId: () => string;
|
||||
getEmail: () => string;
|
||||
getEmailVerified: () => boolean;
|
||||
getName: () => string;
|
||||
getPremium: () => boolean;
|
||||
getIssuer: () => string;
|
||||
getIsExternal: () => boolean;
|
||||
clearToken: (userId?: string) => Promise<any>;
|
||||
decodeToken: (token?: string) => any;
|
||||
getTokenExpirationDate: () => Promise<Date>;
|
||||
tokenSecondsRemaining: (offsetSeconds?: number) => Promise<number>;
|
||||
tokenNeedsRefresh: (minutes?: number) => Promise<boolean>;
|
||||
getUserId: () => Promise<string>;
|
||||
getEmail: () => Promise<string>;
|
||||
getEmailVerified: () => Promise<boolean>;
|
||||
getName: () => Promise<string>;
|
||||
getPremium: () => Promise<boolean>;
|
||||
getIssuer: () => Promise<string>;
|
||||
getIsExternal: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { OrganizationData } from '../models/data/organizationData';
|
||||
import { ProviderData } from '../models/data/providerData';
|
||||
|
||||
import { Organization } from '../models/domain/organization';
|
||||
import { Provider } from '../models/domain/provider';
|
||||
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
|
||||
export abstract class UserService {
|
||||
setInformation: (userId: string, email: string, kdf: KdfType, kdfIterations: number) => Promise<any>;
|
||||
setEmailVerified: (emailVerified: boolean) => Promise<any>;
|
||||
setSecurityStamp: (stamp: string) => Promise<any>;
|
||||
setForcePasswordReset: (forcePasswordReset: boolean) => Promise<any>;
|
||||
getUserId: () => Promise<string>;
|
||||
getEmail: () => Promise<string>;
|
||||
getSecurityStamp: () => Promise<string>;
|
||||
getKdf: () => Promise<KdfType>;
|
||||
getKdfIterations: () => Promise<number>;
|
||||
getEmailVerified: () => Promise<boolean>;
|
||||
getForcePasswordReset: () => Promise<boolean>;
|
||||
clear: () => Promise<any>;
|
||||
isAuthenticated: () => Promise<boolean>;
|
||||
canAccessPremium: () => Promise<boolean>;
|
||||
canManageSponsorships: () => Promise<boolean>;
|
||||
getOrganization: (id: string) => Promise<Organization>;
|
||||
getOrganizationByIdentifier: (identifier: string) => Promise<Organization>;
|
||||
getAllOrganizations: () => Promise<Organization[]>;
|
||||
replaceOrganizations: (organizations: { [id: string]: OrganizationData; }) => Promise<any>;
|
||||
clearOrganizations: (userId: string) => Promise<any>;
|
||||
getProvider: (id: string) => Promise<Provider>;
|
||||
getAllProviders: () => Promise<Provider[]>;
|
||||
replaceProviders: (providers: { [id: string]: ProviderData; }) => Promise<any>;
|
||||
clearProviders: (userId: string) => Promise<any>;
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
import { EncString } from '../models/domain/encString';
|
||||
|
||||
export abstract class VaultTimeoutService {
|
||||
biometricLocked: boolean;
|
||||
everBeenUnlocked: boolean;
|
||||
pinProtectedKey: EncString;
|
||||
isLocked: () => Promise<boolean>;
|
||||
isLocked: (userId?: string) => Promise<boolean>;
|
||||
checkVaultTimeout: () => Promise<void>;
|
||||
lock: (allowSoftLock?: boolean) => Promise<void>;
|
||||
logOut: () => Promise<void>;
|
||||
lock: (allowSoftLock?: boolean, userId?: string) => Promise<void>;
|
||||
logOut: (userId?: string) => Promise<void>;
|
||||
setVaultTimeoutOptions: (vaultTimeout: number, vaultTimeoutAction: string) => Promise<void>;
|
||||
getVaultTimeout: () => Promise<number>;
|
||||
isPinLockSet: () => Promise<[boolean, boolean]>;
|
||||
isBiometricLockSet: () => Promise<boolean>;
|
||||
clear: () => Promise<any>;
|
||||
clear: (userId?: string) => Promise<any>;
|
||||
}
|
||||
|
||||
6
common/src/enums/authenticationStatus.ts
Normal file
6
common/src/enums/authenticationStatus.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum AuthenticationStatus {
|
||||
Locked = 'locked',
|
||||
Unlocked = 'unlocked',
|
||||
LoggedOut = 'loggedOut',
|
||||
Active = 'active',
|
||||
}
|
||||
5
common/src/enums/htmlStorageLocation.ts
Normal file
5
common/src/enums/htmlStorageLocation.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum HtmlStorageLocation {
|
||||
Local = 'local',
|
||||
Memory = 'memory',
|
||||
Session = 'session',
|
||||
}
|
||||
4
common/src/enums/keySuffixOptions.ts
Normal file
4
common/src/enums/keySuffixOptions.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum KeySuffixOptions {
|
||||
Auto = 'auto',
|
||||
Biometric = 'biometric',
|
||||
}
|
||||
5
common/src/enums/storageLocation.ts
Normal file
5
common/src/enums/storageLocation.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum StorageLocation {
|
||||
Both = 'both',
|
||||
Disk = 'disk',
|
||||
Memory = 'memory',
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export class FSecureFskImporter extends BaseImporter implements Importer {
|
||||
cipher.name = this.getValueOrDefault(value.service);
|
||||
cipher.notes = this.getValueOrDefault(value.notes);
|
||||
|
||||
if (value.style === 'website') {
|
||||
if (value.style === 'website' || value.style === 'globe') {
|
||||
cipher.login.username = this.getValueOrDefault(value.username);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
cipher.login.uris = this.makeUriArray(value.url);
|
||||
|
||||
168
common/src/models/domain/account.ts
Normal file
168
common/src/models/domain/account.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import { OrganizationData } from '../data/organizationData';
|
||||
|
||||
import { AuthenticationStatus } from '../../enums/authenticationStatus';
|
||||
import { KdfType } from '../../enums/kdfType';
|
||||
import { UriMatchType } from '../../enums/uriMatchType';
|
||||
|
||||
import { CipherView } from '../view/cipherView';
|
||||
import { CollectionView } from '../view/collectionView';
|
||||
import { FolderView } from '../view/folderView';
|
||||
import { SendView } from '../view/sendView';
|
||||
|
||||
import { EncString } from './encString';
|
||||
import { GeneratedPasswordHistory } from './generatedPasswordHistory';
|
||||
import { Policy } from './policy';
|
||||
import { SymmetricCryptoKey } from './symmetricCryptoKey';
|
||||
|
||||
import { CipherData } from '../data/cipherData';
|
||||
import { CollectionData } from '../data/collectionData';
|
||||
import { EventData } from '../data/eventData';
|
||||
import { FolderData } from '../data/folderData';
|
||||
import { PolicyData } from '../data/policyData';
|
||||
import { ProviderData } from '../data/providerData';
|
||||
import { SendData } from '../data/sendData';
|
||||
|
||||
export class EncryptionPair<TEncrypted, TDecrypted> {
|
||||
encrypted?: TEncrypted;
|
||||
decrypted?: TDecrypted;
|
||||
}
|
||||
|
||||
export class DataEncryptionPair<TEncrypted, TDecrypted> {
|
||||
encrypted?: { [id: string]: TEncrypted };
|
||||
decrypted?: TDecrypted[];
|
||||
}
|
||||
|
||||
export class AccountData {
|
||||
ciphers?: DataEncryptionPair<CipherData, CipherView> = new DataEncryptionPair<CipherData, CipherView>();
|
||||
folders?: DataEncryptionPair<FolderData, FolderView> = new DataEncryptionPair<FolderData, FolderView>();
|
||||
localData?: any;
|
||||
sends?: DataEncryptionPair<SendData, SendView> = new DataEncryptionPair<SendData, SendView>();
|
||||
collections?: DataEncryptionPair<CollectionData, CollectionView> = new DataEncryptionPair<CollectionData, CollectionView>();
|
||||
policies?: DataEncryptionPair<PolicyData, Policy> = new DataEncryptionPair<PolicyData, Policy>();
|
||||
passwordGenerationHistory?: EncryptionPair<GeneratedPasswordHistory[], GeneratedPasswordHistory[]> = new EncryptionPair<GeneratedPasswordHistory[], GeneratedPasswordHistory[]>();
|
||||
addEditCipherInfo?: any;
|
||||
collapsedGroupings?: Set<string>;
|
||||
eventCollection?: EventData[];
|
||||
organizations?: { [id: string]: OrganizationData };
|
||||
providers?: { [id: string]: ProviderData };
|
||||
}
|
||||
|
||||
export class AccountKeys {
|
||||
cryptoMasterKey?: SymmetricCryptoKey;
|
||||
cryptoMasterKeyAuto?: string;
|
||||
cryptoMasterKeyB64?: string;
|
||||
cryptoMasterKeyBiometric?: string;
|
||||
cryptoSymmetricKey?: EncryptionPair<string, SymmetricCryptoKey> = new EncryptionPair<string, SymmetricCryptoKey>();
|
||||
organizationKeys?: EncryptionPair<any, Map<string, SymmetricCryptoKey>> = new EncryptionPair<any, Map<string, SymmetricCryptoKey>>();
|
||||
providerKeys?: EncryptionPair<any, Map<string, SymmetricCryptoKey>> = new EncryptionPair<any, Map<string, SymmetricCryptoKey>>();
|
||||
privateKey?: EncryptionPair<string, ArrayBuffer> = new EncryptionPair<string, ArrayBuffer>();
|
||||
legacyEtmKey?: SymmetricCryptoKey;
|
||||
publicKey?: ArrayBuffer;
|
||||
apiKeyClientSecret?: string;
|
||||
}
|
||||
|
||||
export class AccountProfile {
|
||||
apiKeyClientId?: string;
|
||||
authenticationStatus?: AuthenticationStatus;
|
||||
convertAccountToKeyConnector?: boolean;
|
||||
email?: string;
|
||||
emailVerified?: boolean;
|
||||
entityId?: string;
|
||||
entityType?: string;
|
||||
everBeenUnlocked?: boolean;
|
||||
forcePasswordReset?: boolean;
|
||||
hasPremiumPersonally?: boolean;
|
||||
lastActive?: number;
|
||||
lastSync?: string;
|
||||
ssoCodeVerifier?: string;
|
||||
ssoOrganizationIdentifier?: string;
|
||||
ssoState?: string;
|
||||
userId?: string;
|
||||
usesKeyConnector?: boolean;
|
||||
keyHash?: string;
|
||||
kdfIterations?: number;
|
||||
kdfType?: KdfType;
|
||||
}
|
||||
|
||||
export class AccountSettings {
|
||||
alwaysShowDock?: boolean;
|
||||
autoConfirmFingerPrints?: boolean;
|
||||
autoFillOnPageLoadDefault?: boolean;
|
||||
biometricLocked?: boolean;
|
||||
biometricUnlock?: boolean;
|
||||
clearClipboard?: number;
|
||||
defaultUriMatch?: UriMatchType;
|
||||
disableAddLoginNotification?: boolean;
|
||||
disableAutoBiometricsPrompt?: boolean;
|
||||
disableAutoTotpCopy?: boolean;
|
||||
disableBadgeCounter?: boolean;
|
||||
disableChangedPasswordNotification?: boolean;
|
||||
disableContextMenuItem?: boolean;
|
||||
disableGa?: boolean;
|
||||
dontShowCardsCurrentTab?: boolean;
|
||||
dontShowIdentitiesCurrentTab?: boolean;
|
||||
enableAlwaysOnTop?: boolean;
|
||||
enableAutoFillOnPageLoad?: boolean;
|
||||
enableBiometric?: boolean;
|
||||
enableBrowserIntegration?: boolean;
|
||||
enableBrowserIntegrationFingerprint?: boolean;
|
||||
enableCloseToTray?: boolean;
|
||||
enableFullWidth?: boolean;
|
||||
enableGravitars?: boolean;
|
||||
enableMinimizeToTray?: boolean;
|
||||
enableStartToTray?: boolean;
|
||||
enableTray?: boolean;
|
||||
environmentUrls?: any = {
|
||||
server: 'bitwarden.com',
|
||||
};
|
||||
equivalentDomains?: any;
|
||||
minimizeOnCopyToClipboard?: boolean;
|
||||
neverDomains?: { [id: string]: any };
|
||||
openAtLogin?: boolean;
|
||||
passwordGenerationOptions?: any;
|
||||
pinProtected?: EncryptionPair<string, EncString> = new EncryptionPair<string, EncString>();
|
||||
protectedPin?: string;
|
||||
settings?: any; // TODO: Merge whatever is going on here into the AccountSettings model properly
|
||||
vaultTimeout?: number;
|
||||
vaultTimeoutAction?: string;
|
||||
}
|
||||
|
||||
export class AccountTokens {
|
||||
accessToken?: string;
|
||||
decodedToken?: any;
|
||||
refreshToken?: string;
|
||||
securityStamp?: string;
|
||||
}
|
||||
|
||||
export class Account {
|
||||
data?: AccountData = new AccountData();
|
||||
keys?: AccountKeys = new AccountKeys();
|
||||
profile?: AccountProfile = new AccountProfile();
|
||||
settings?: AccountSettings = new AccountSettings();
|
||||
tokens?: AccountTokens = new AccountTokens();
|
||||
|
||||
constructor(init: Partial<Account>) {
|
||||
Object.assign(this, {
|
||||
data: {
|
||||
...new AccountData(),
|
||||
...init?.data,
|
||||
},
|
||||
keys: {
|
||||
...new AccountKeys(),
|
||||
...init?.keys,
|
||||
},
|
||||
profile: {
|
||||
...new AccountProfile(),
|
||||
...init?.profile,
|
||||
},
|
||||
settings: {
|
||||
...new AccountSettings(),
|
||||
...init?.settings,
|
||||
},
|
||||
tokens: {
|
||||
...new AccountTokens(),
|
||||
...init?.tokens,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
24
common/src/models/domain/globalState.ts
Normal file
24
common/src/models/domain/globalState.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export class GlobalState {
|
||||
enableAlwaysOnTop?: boolean;
|
||||
installedVersion?: string;
|
||||
lastActive?: number;
|
||||
locale?: string;
|
||||
openAtLogin?: boolean;
|
||||
organizationInvitation?: any;
|
||||
rememberedEmail?: string;
|
||||
theme?: string;
|
||||
window?: Map<string, any> = new Map<string, any>();
|
||||
twoFactorToken?: string;
|
||||
disableFavicon?: boolean;
|
||||
biometricAwaitingAcceptance?: boolean;
|
||||
biometricFingerprintValidated?: boolean;
|
||||
vaultTimeout?: number;
|
||||
vaultTimeoutAction?: string;
|
||||
loginRedirect?: any;
|
||||
mainWindowSize?: number;
|
||||
enableBiometrics?: boolean;
|
||||
biometricText?: string;
|
||||
noAutoPromptBiometrics?: boolean;
|
||||
noAutoPromptBiometricsText?: string;
|
||||
stateVersion: number;
|
||||
}
|
||||
9
common/src/models/domain/state.ts
Normal file
9
common/src/models/domain/state.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Account } from './account';
|
||||
import { GlobalState } from './globalState';
|
||||
|
||||
export class State {
|
||||
accounts: { [userId: string]: Account } = {};
|
||||
globals: GlobalState = new GlobalState();
|
||||
activeUserId: string;
|
||||
}
|
||||
|
||||
10
common/src/models/domain/storageOptions.ts
Normal file
10
common/src/models/domain/storageOptions.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { HtmlStorageLocation } from '../../enums/htmlStorageLocation';
|
||||
import { StorageLocation } from '../../enums/storageLocation';
|
||||
|
||||
export type StorageOptions = {
|
||||
storageLocation?: StorageLocation;
|
||||
useSecureStorage?: boolean;
|
||||
userId?: string;
|
||||
htmlStorageLocation?: HtmlStorageLocation;
|
||||
keySuffix?: string,
|
||||
};
|
||||
@@ -1515,7 +1515,7 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
|
||||
async getActiveBearerToken(): Promise<string> {
|
||||
let accessToken = await this.tokenService.getToken();
|
||||
if (this.tokenService.tokenNeedsRefresh()) {
|
||||
if (await this.tokenService.tokenNeedsRefresh()) {
|
||||
await this.doAuthRefresh();
|
||||
accessToken = await this.tokenService.getToken();
|
||||
}
|
||||
@@ -1637,7 +1637,7 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
headers.set('User-Agent', this.customUserAgent);
|
||||
}
|
||||
|
||||
const decodedToken = this.tokenService.decodeToken();
|
||||
const decodedToken = await this.tokenService.decodeToken();
|
||||
const response = await this.fetch(new Request(this.environmentService.getIdentityUrl() + '/connect/token', {
|
||||
body: this.qsStringify({
|
||||
grant_type: 'refresh_token',
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
import { ApiKeyService as ApiKeyServiceAbstraction } from '../abstractions/apiKey.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { TokenService } from '../abstractions/token.service';
|
||||
|
||||
import { Utils } from '../misc/utils';
|
||||
|
||||
const Keys = {
|
||||
clientId: 'clientId',
|
||||
clientSecret: 'clientSecret',
|
||||
entityType: 'entityType',
|
||||
entityId: 'entityId',
|
||||
};
|
||||
|
||||
|
||||
export class ApiKeyService implements ApiKeyServiceAbstraction {
|
||||
private clientId: string;
|
||||
private clientSecret: string;
|
||||
private entityType: string;
|
||||
private entityId: string;
|
||||
|
||||
constructor(private tokenService: TokenService, private storageService: StorageService) { }
|
||||
|
||||
async setInformation(clientId: string, clientSecret: string) {
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
const idParts = clientId.split('.');
|
||||
|
||||
if (idParts.length !== 2 || !Utils.isGuid(idParts[1])) {
|
||||
throw Error('Invalid clientId');
|
||||
}
|
||||
this.entityType = idParts[0];
|
||||
this.entityId = idParts[1];
|
||||
|
||||
await this.storageService.save(Keys.clientId, this.clientId);
|
||||
await this.storageService.save(Keys.entityId, this.entityId);
|
||||
await this.storageService.save(Keys.entityType, this.entityType);
|
||||
await this.storageService.save(Keys.clientSecret, this.clientSecret);
|
||||
}
|
||||
|
||||
async getClientId(): Promise<string> {
|
||||
if (this.clientId == null) {
|
||||
this.clientId = await this.storageService.get<string>(Keys.clientId);
|
||||
}
|
||||
return this.clientId;
|
||||
}
|
||||
|
||||
async getClientSecret(): Promise<string> {
|
||||
if (this.clientSecret == null) {
|
||||
this.clientSecret = await this.storageService.get<string>(Keys.clientSecret);
|
||||
}
|
||||
return this.clientSecret;
|
||||
}
|
||||
|
||||
async getEntityType(): Promise<string> {
|
||||
if (this.entityType == null) {
|
||||
this.entityType = await this.storageService.get<string>(Keys.entityType);
|
||||
}
|
||||
return this.entityType;
|
||||
}
|
||||
|
||||
async getEntityId(): Promise<string> {
|
||||
if (this.entityId == null) {
|
||||
this.entityId = await this.storageService.get<string>(Keys.entityId);
|
||||
}
|
||||
return this.entityId;
|
||||
}
|
||||
|
||||
async clear(): Promise<any> {
|
||||
await this.storageService.remove(Keys.clientId);
|
||||
await this.storageService.remove(Keys.clientSecret);
|
||||
await this.storageService.remove(Keys.entityId);
|
||||
await this.storageService.remove(Keys.entityType);
|
||||
|
||||
this.clientId = this.clientSecret = this.entityId = this.entityType = null;
|
||||
}
|
||||
|
||||
async isAuthenticated(): Promise<boolean> {
|
||||
const token = await this.tokenService.getToken();
|
||||
if (token == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const entityId = await this.getEntityId();
|
||||
return entityId != null;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { HashPurpose } from '../enums/hashPurpose';
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
import { TwoFactorProviderType } from '../enums/twoFactorProviderType';
|
||||
|
||||
import { Account, AccountData, AccountProfile, AccountTokens } from '../models/domain/account';
|
||||
import { AuthResult } from '../models/domain/authResult';
|
||||
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
|
||||
@@ -26,8 +27,8 @@ import { KeyConnectorService } from '../abstractions/keyConnector.service';
|
||||
import { LogService } from '../abstractions/log.service';
|
||||
import { MessagingService } from '../abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
import { TokenService } from '../abstractions/token.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
import { VaultTimeoutService } from '../abstractions/vaultTimeout.service';
|
||||
|
||||
import { Utils } from '../misc/utils';
|
||||
@@ -99,12 +100,12 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
private key: SymmetricCryptoKey;
|
||||
|
||||
constructor(private cryptoService: CryptoService, protected apiService: ApiService,
|
||||
private userService: UserService, protected tokenService: TokenService,
|
||||
protected appIdService: AppIdService, private i18nService: I18nService,
|
||||
protected platformUtilsService: PlatformUtilsService, private messagingService: MessagingService,
|
||||
private vaultTimeoutService: VaultTimeoutService, private logService: LogService,
|
||||
private cryptoFunctionService: CryptoFunctionService, private environmentService: EnvironmentService,
|
||||
private keyConnectorService: KeyConnectorService, private setCryptoKeys = true) {
|
||||
protected tokenService: TokenService, protected appIdService: AppIdService,
|
||||
private i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
|
||||
private messagingService: MessagingService, private vaultTimeoutService: VaultTimeoutService,
|
||||
private logService: LogService, protected cryptoFunctionService: CryptoFunctionService,
|
||||
private keyConnectorService: KeyConnectorService, protected environmentService: EnvironmentService,
|
||||
protected stateService: StateService, private setCryptoKeys = true) {
|
||||
}
|
||||
|
||||
init() {
|
||||
@@ -234,7 +235,7 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
|
||||
let providerType: TwoFactorProviderType = null;
|
||||
let providerPriority = -1;
|
||||
this.twoFactorProvidersData.forEach((value, type) => {
|
||||
this.twoFactorProvidersData.forEach((_value, type) => {
|
||||
const provider = (TwoFactorProviders as any)[type];
|
||||
if (provider != null && provider.priority > providerPriority) {
|
||||
if (type === TwoFactorProviderType.WebAuthn && !webAuthnSupported) {
|
||||
@@ -350,13 +351,34 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
const tokenResponse = response as IdentityTokenResponse;
|
||||
result.resetMasterPassword = tokenResponse.resetMasterPassword;
|
||||
result.forcePasswordReset = tokenResponse.forcePasswordReset;
|
||||
|
||||
const accountInformation = await this.tokenService.decodeToken(tokenResponse.accessToken);
|
||||
await this.stateService.addAccount({
|
||||
profile: {
|
||||
...new AccountProfile(),
|
||||
...{
|
||||
userId: accountInformation.sub,
|
||||
email: accountInformation.email,
|
||||
apiKeyClientId: clientId,
|
||||
apiKeyClientSecret: clientSecret,
|
||||
hasPremiumPersonally: accountInformation.premium,
|
||||
kdfIterations: tokenResponse.kdfIterations,
|
||||
kdfType: tokenResponse.kdf,
|
||||
},
|
||||
},
|
||||
tokens: {
|
||||
...new AccountTokens(),
|
||||
...{
|
||||
accessToken: tokenResponse.accessToken,
|
||||
refreshToken: tokenResponse.refreshToken,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (tokenResponse.twoFactorToken != null) {
|
||||
await this.tokenService.setTwoFactorToken(tokenResponse.twoFactorToken, email);
|
||||
}
|
||||
|
||||
await this.tokenService.setTokens(tokenResponse.accessToken, tokenResponse.refreshToken, clientIdClientSecret);
|
||||
await this.userService.setInformation(this.tokenService.getUserId(), this.tokenService.getEmail(),
|
||||
tokenResponse.kdf, tokenResponse.kdfIterations);
|
||||
if (this.setCryptoKeys) {
|
||||
if (key != null) {
|
||||
await this.cryptoService.setKey(key);
|
||||
@@ -392,7 +414,7 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
} else if (tokenResponse.keyConnectorUrl != null) {
|
||||
const password = await this.cryptoFunctionService.randomBytes(64);
|
||||
|
||||
const k = await this.cryptoService.makeKey(Utils.fromBufferToB64(password), this.tokenService.getEmail(), tokenResponse.kdf, tokenResponse.kdfIterations);
|
||||
const k = await this.cryptoService.makeKey(Utils.fromBufferToB64(password), await this.tokenService.getEmail(), tokenResponse.kdf, tokenResponse.kdfIterations);
|
||||
const keyConnectorRequest = new KeyConnectorUserKeyRequest(k.encKeyB64);
|
||||
await this.cryptoService.setKey(k);
|
||||
|
||||
@@ -416,7 +438,7 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
}
|
||||
|
||||
if (this.vaultTimeoutService != null) {
|
||||
this.vaultTimeoutService.biometricLocked = false;
|
||||
await this.stateService.setBiometricLocked(false);
|
||||
}
|
||||
this.messagingService.send('loggedIn');
|
||||
return result;
|
||||
|
||||
@@ -46,43 +46,32 @@ import { FileUploadService } from '../abstractions/fileUpload.service';
|
||||
import { I18nService } from '../abstractions/i18n.service';
|
||||
import { SearchService } from '../abstractions/search.service';
|
||||
import { SettingsService } from '../abstractions/settings.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
|
||||
import { ConstantsService } from './constants.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
import { LogService } from '../abstractions/log.service';
|
||||
import { sequentialize } from '../misc/sequentialize';
|
||||
import { Utils } from '../misc/utils';
|
||||
|
||||
const Keys = {
|
||||
ciphersPrefix: 'ciphers_',
|
||||
localData: 'sitesLocalData',
|
||||
neverDomains: 'neverDomains',
|
||||
};
|
||||
|
||||
const DomainMatchBlacklist = new Map<string, Set<string>>([
|
||||
['google.com', new Set(['script.google.com'])],
|
||||
]);
|
||||
|
||||
export class CipherService implements CipherServiceAbstraction {
|
||||
// tslint:disable-next-line
|
||||
_decryptedCipherCache: CipherView[];
|
||||
|
||||
private sortedCiphersCache: SortedCiphersCache = new SortedCiphersCache(this.sortCiphersByLastUsed);
|
||||
|
||||
constructor(private cryptoService: CryptoService, private userService: UserService,
|
||||
private settingsService: SettingsService, private apiService: ApiService,
|
||||
private fileUploadService: FileUploadService, private storageService: StorageService,
|
||||
constructor(private cryptoService: CryptoService, private settingsService: SettingsService,
|
||||
private apiService: ApiService, private fileUploadService: FileUploadService,
|
||||
private i18nService: I18nService, private searchService: () => SearchService,
|
||||
private logService: LogService) {
|
||||
private logService: LogService, private stateService: StateService) {
|
||||
}
|
||||
|
||||
get decryptedCipherCache() {
|
||||
return this._decryptedCipherCache;
|
||||
async getDecryptedCipherCache(): Promise<CipherView[]> {
|
||||
const decryptedCiphers = await this.stateService.getDecryptedCiphers();
|
||||
return decryptedCiphers;
|
||||
}
|
||||
set decryptedCipherCache(value: CipherView[]) {
|
||||
this._decryptedCipherCache = value;
|
||||
|
||||
async setDecryptedCipherCache(value: CipherView[]) {
|
||||
await this.stateService.setDecryptedCiphers(value);
|
||||
if (this.searchService != null) {
|
||||
if (value == null) {
|
||||
this.searchService().clearIndex();
|
||||
@@ -92,9 +81,8 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
}
|
||||
|
||||
clearCache(): void {
|
||||
this.decryptedCipherCache = null;
|
||||
this.sortedCiphersCache.clear();
|
||||
async clearCache(userId?: string): Promise<void> {
|
||||
await this.clearDecryptedCiphersState(userId);
|
||||
}
|
||||
|
||||
async encrypt(model: CipherView, key?: SymmetricCryptoKey, originalCipher: Cipher = null): Promise<Cipher> {
|
||||
@@ -212,12 +200,10 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
const self = this;
|
||||
const encFields: Field[] = [];
|
||||
await fieldsModel.reduce((promise, field) => {
|
||||
return promise.then(() => {
|
||||
return self.encryptField(field, key);
|
||||
}).then((encField: Field) => {
|
||||
encFields.push(encField);
|
||||
});
|
||||
await fieldsModel.reduce(async (promise, field) => {
|
||||
await promise;
|
||||
const encField = await self.encryptField(field, key);
|
||||
encFields.push(encField);
|
||||
}, Promise.resolve());
|
||||
|
||||
return encFields;
|
||||
@@ -247,12 +233,10 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
const self = this;
|
||||
const encPhs: Password[] = [];
|
||||
await phModels.reduce((promise, ph) => {
|
||||
return promise.then(() => {
|
||||
return self.encryptPasswordHistory(ph, key);
|
||||
}).then((encPh: Password) => {
|
||||
encPhs.push(encPh);
|
||||
});
|
||||
await phModels.reduce(async (promise, ph) => {
|
||||
await promise;
|
||||
const encPh = await self.encryptPasswordHistory(ph, key);
|
||||
encPhs.push(encPh);
|
||||
}, Promise.resolve());
|
||||
|
||||
return encPhs;
|
||||
@@ -270,22 +254,18 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
async get(id: string): Promise<Cipher> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const localData = await this.storageService.get<any>(Keys.localData);
|
||||
const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||
Keys.ciphersPrefix + userId);
|
||||
const ciphers = await this.stateService.getEncryptedCiphers();
|
||||
if (ciphers == null || !ciphers.hasOwnProperty(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const localData = await this.stateService.getLocalData();
|
||||
return new Cipher(ciphers[id], false, localData ? localData[id] : null);
|
||||
}
|
||||
|
||||
async getAll(): Promise<Cipher[]> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const localData = await this.storageService.get<any>(Keys.localData);
|
||||
const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||
Keys.ciphersPrefix + userId);
|
||||
const localData = await this.stateService.getLocalData();
|
||||
const ciphers = await this.stateService.getEncryptedCiphers();
|
||||
const response: Cipher[] = [];
|
||||
for (const id in ciphers) {
|
||||
if (ciphers.hasOwnProperty(id)) {
|
||||
@@ -297,13 +277,13 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
@sequentialize(() => 'getAllDecrypted')
|
||||
async getAllDecrypted(): Promise<CipherView[]> {
|
||||
if (this.decryptedCipherCache != null) {
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
if (await this.getDecryptedCipherCache() != null) {
|
||||
if (this.searchService != null && (this.searchService().indexedEntityId ?? userId) !== userId)
|
||||
{
|
||||
await this.searchService().indexCiphers(userId, this.decryptedCipherCache);
|
||||
await this.searchService().indexCiphers(userId, await this.getDecryptedCipherCache());
|
||||
}
|
||||
return this.decryptedCipherCache;
|
||||
return await this.getDecryptedCipherCache();
|
||||
}
|
||||
|
||||
const decCiphers: CipherView[] = [];
|
||||
@@ -314,14 +294,14 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
const promises: any[] = [];
|
||||
const ciphers = await this.getAll();
|
||||
ciphers.forEach(cipher => {
|
||||
ciphers.forEach(async cipher => {
|
||||
promises.push(cipher.decrypt().then(c => decCiphers.push(c)));
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
decCiphers.sort(this.getLocaleSortingFunction());
|
||||
this.decryptedCipherCache = decCiphers;
|
||||
return this.decryptedCipherCache;
|
||||
await this.stateService.setDecryptedCiphers(decCiphers);
|
||||
return decCiphers;
|
||||
}
|
||||
|
||||
async getAllDecryptedForGrouping(groupingId: string, folder: boolean = true): Promise<CipherView[]> {
|
||||
@@ -369,7 +349,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
const ciphers = result[1];
|
||||
|
||||
if (defaultMatch == null) {
|
||||
defaultMatch = await this.storageService.get<UriMatchType>(ConstantsService.defaultUriMatch);
|
||||
defaultMatch = await this.stateService.getDefaultUriMatch();
|
||||
if (defaultMatch == null) {
|
||||
defaultMatch = UriMatchType.Domain;
|
||||
}
|
||||
@@ -476,7 +456,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
async updateLastUsedDate(id: string): Promise<void> {
|
||||
let ciphersLocalData = await this.storageService.get<any>(Keys.localData);
|
||||
let ciphersLocalData = await this.stateService.getLocalData();
|
||||
if (!ciphersLocalData) {
|
||||
ciphersLocalData = {};
|
||||
}
|
||||
@@ -489,23 +469,25 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
};
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.localData, ciphersLocalData);
|
||||
await this.stateService.setLocalData(ciphersLocalData);
|
||||
|
||||
if (this.decryptedCipherCache == null) {
|
||||
const decryptedCipherCache = await this.stateService.getDecryptedCiphers();
|
||||
if (!decryptedCipherCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.decryptedCipherCache.length; i++) {
|
||||
const cached = this.decryptedCipherCache[i];
|
||||
for (let i = 0; i < decryptedCipherCache.length; i++) {
|
||||
const cached = decryptedCipherCache[i];
|
||||
if (cached.id === id) {
|
||||
cached.localData = ciphersLocalData[id];
|
||||
break;
|
||||
}
|
||||
}
|
||||
await this.stateService.setDecryptedCiphers(decryptedCipherCache);
|
||||
}
|
||||
|
||||
async updateLastLaunchedDate(id: string): Promise<void> {
|
||||
let ciphersLocalData = await this.storageService.get<any>(Keys.localData);
|
||||
let ciphersLocalData = await this.stateService.getLocalData();
|
||||
if (!ciphersLocalData) {
|
||||
ciphersLocalData = {};
|
||||
}
|
||||
@@ -518,19 +500,21 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
};
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.localData, ciphersLocalData);
|
||||
await this.stateService.setLocalData(ciphersLocalData);
|
||||
|
||||
if (this.decryptedCipherCache == null) {
|
||||
const decryptedCipherCache = await this.stateService.getDecryptedCiphers();
|
||||
if (!decryptedCipherCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.decryptedCipherCache.length; i++) {
|
||||
const cached = this.decryptedCipherCache[i];
|
||||
for (let i = 0; i < decryptedCipherCache.length; i++) {
|
||||
const cached = decryptedCipherCache[i];
|
||||
if (cached.id === id) {
|
||||
cached.localData = ciphersLocalData[id];
|
||||
break;
|
||||
}
|
||||
}
|
||||
await this.stateService.setDecryptedCiphers(decryptedCipherCache);
|
||||
}
|
||||
|
||||
async saveNeverDomain(domain: string): Promise<void> {
|
||||
@@ -538,12 +522,12 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
return;
|
||||
}
|
||||
|
||||
let domains = await this.storageService.get<{ [id: string]: any; }>(Keys.neverDomains);
|
||||
let domains = await this.stateService.getNeverDomains();
|
||||
if (!domains) {
|
||||
domains = {};
|
||||
}
|
||||
domains[domain] = null;
|
||||
await this.storageService.save(Keys.neverDomains, domains);
|
||||
await this.stateService.setNeverDomains(domains);
|
||||
}
|
||||
|
||||
async saveWithServer(cipher: Cipher): Promise<any> {
|
||||
@@ -562,8 +546,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
response = await this.apiService.putCipher(cipher.id, request);
|
||||
}
|
||||
|
||||
const userId = await this.userService.getUserId();
|
||||
const data = new CipherData(response, userId, cipher.collectionIds);
|
||||
const data = new CipherData(response, await this.stateService.getUserId(), cipher.collectionIds);
|
||||
await this.upsert(data);
|
||||
}
|
||||
|
||||
@@ -583,8 +566,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
const encCipher = await this.encrypt(cipher);
|
||||
const request = new CipherShareRequest(encCipher);
|
||||
const response = await this.apiService.putShareCipher(cipher.id, request);
|
||||
const userId = await this.userService.getUserId();
|
||||
const data = new CipherData(response, userId, collectionIds);
|
||||
const data = new CipherData(response, await this.stateService.getUserId(), collectionIds);
|
||||
await this.upsert(data);
|
||||
}
|
||||
|
||||
@@ -601,7 +583,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
await Promise.all(promises);
|
||||
const request = new CipherBulkShareRequest(encCiphers, collectionIds);
|
||||
await this.apiService.putShareCiphers(request);
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
await this.upsert(encCiphers.map(c => c.toCipherData(userId)));
|
||||
}
|
||||
|
||||
@@ -618,7 +600,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
reject(e);
|
||||
}
|
||||
};
|
||||
reader.onerror = evt => {
|
||||
reader.onerror = _evt => {
|
||||
reject('Error reading file.');
|
||||
};
|
||||
});
|
||||
@@ -654,8 +636,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
}
|
||||
|
||||
const userId = await this.userService.getUserId();
|
||||
const cData = new CipherData(response, userId, cipher.collectionIds);
|
||||
const cData = new CipherData(response, await this.stateService.getUserId(), cipher.collectionIds);
|
||||
if (!admin) {
|
||||
await this.upsert(cData);
|
||||
}
|
||||
@@ -702,15 +683,12 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
async saveCollectionsWithServer(cipher: Cipher): Promise<any> {
|
||||
const request = new CipherCollectionsRequest(cipher.collectionIds);
|
||||
await this.apiService.putCipherCollections(cipher.id, request);
|
||||
const userId = await this.userService.getUserId();
|
||||
const data = cipher.toCipherData(userId);
|
||||
const data = cipher.toCipherData(await this.stateService.getUserId());
|
||||
await this.upsert(data);
|
||||
}
|
||||
|
||||
async upsert(cipher: CipherData | CipherData[]): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
let ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||
Keys.ciphersPrefix + userId);
|
||||
let ciphers = await this.stateService.getEncryptedCiphers();
|
||||
if (ciphers == null) {
|
||||
ciphers = {};
|
||||
}
|
||||
@@ -724,27 +702,23 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.ciphersPrefix + userId, ciphers);
|
||||
this.decryptedCipherCache = null;
|
||||
await this.replace(ciphers);
|
||||
}
|
||||
|
||||
async replace(ciphers: { [id: string]: CipherData; }): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
await this.storageService.save(Keys.ciphersPrefix + userId, ciphers);
|
||||
this.decryptedCipherCache = null;
|
||||
await this.clearDecryptedCiphersState();
|
||||
await this.stateService.setEncryptedCiphers(ciphers);
|
||||
}
|
||||
|
||||
async clear(userId: string): Promise<any> {
|
||||
await this.storageService.remove(Keys.ciphersPrefix + userId);
|
||||
this.clearCache();
|
||||
async clear(userId?: string): Promise<any> {
|
||||
await this.clearEncryptedCiphersState(userId);
|
||||
await this.clearCache(userId);
|
||||
}
|
||||
|
||||
async moveManyWithServer(ids: string[], folderId: string): Promise<any> {
|
||||
await this.apiService.putMoveCiphers(new CipherBulkMoveRequest(ids, folderId));
|
||||
|
||||
const userId = await this.userService.getUserId();
|
||||
let ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||
Keys.ciphersPrefix + userId);
|
||||
let ciphers = await this.stateService.getEncryptedCiphers();
|
||||
if (ciphers == null) {
|
||||
ciphers = {};
|
||||
}
|
||||
@@ -755,14 +729,12 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
});
|
||||
|
||||
await this.storageService.save(Keys.ciphersPrefix + userId, ciphers);
|
||||
this.decryptedCipherCache = null;
|
||||
await this.clearCache();
|
||||
await this.stateService.setEncryptedCiphers(ciphers);
|
||||
}
|
||||
|
||||
async delete(id: string | string[]): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||
Keys.ciphersPrefix + userId);
|
||||
const ciphers = await this.stateService.getEncryptedCiphers();
|
||||
if (ciphers == null) {
|
||||
return;
|
||||
}
|
||||
@@ -778,8 +750,8 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.ciphersPrefix + userId, ciphers);
|
||||
this.decryptedCipherCache = null;
|
||||
await this.clearCache();
|
||||
await this.stateService.setEncryptedCiphers(ciphers);
|
||||
}
|
||||
|
||||
async deleteWithServer(id: string): Promise<any> {
|
||||
@@ -793,9 +765,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
async deleteAttachment(id: string, attachmentId: string): Promise<void> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||
Keys.ciphersPrefix + userId);
|
||||
const ciphers = await this.stateService.getEncryptedCiphers();
|
||||
|
||||
if (ciphers == null || !ciphers.hasOwnProperty(id) || ciphers[id].attachments == null) {
|
||||
return;
|
||||
@@ -807,8 +777,8 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.ciphersPrefix + userId, ciphers);
|
||||
this.decryptedCipherCache = null;
|
||||
await this.clearCache();
|
||||
await this.stateService.setEncryptedCiphers(ciphers);
|
||||
}
|
||||
|
||||
async deleteAttachmentWithServer(id: string, attachmentId: string): Promise<void> {
|
||||
@@ -887,9 +857,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
async softDelete(id: string | string[]): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||
Keys.ciphersPrefix + userId);
|
||||
const ciphers = await this.stateService.getEncryptedCiphers();
|
||||
if (ciphers == null) {
|
||||
return;
|
||||
}
|
||||
@@ -907,8 +875,8 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
(id as string[]).forEach(setDeletedDate);
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.ciphersPrefix + userId, ciphers);
|
||||
this.decryptedCipherCache = null;
|
||||
await this.clearCache();
|
||||
await this.stateService.setEncryptedCiphers(ciphers);
|
||||
}
|
||||
|
||||
async softDeleteWithServer(id: string): Promise<any> {
|
||||
@@ -922,9 +890,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
async restore(cipher: { id: string, revisionDate: string; } | { id: string, revisionDate: string; }[]) {
|
||||
const userId = await this.userService.getUserId();
|
||||
const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(
|
||||
Keys.ciphersPrefix + userId);
|
||||
const ciphers = await this.stateService.getEncryptedCiphers();
|
||||
if (ciphers == null) {
|
||||
return;
|
||||
}
|
||||
@@ -944,8 +910,8 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
clearDeletedDate(cipher as { id: string, revisionDate: string; });
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.ciphersPrefix + userId, ciphers);
|
||||
this.decryptedCipherCache = null;
|
||||
await this.clearCache();
|
||||
await this.stateService.setEncryptedCiphers(ciphers);
|
||||
}
|
||||
|
||||
async restoreWithServer(id: string): Promise<any> {
|
||||
@@ -1109,7 +1075,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
if (autofillOnPageLoad) {
|
||||
const autofillOnPageLoadDefault = await this.storageService.get(ConstantsService.autoFillOnPageLoadDefaultKey);
|
||||
const autofillOnPageLoadDefault = await this.stateService.getAutoFillOnPageLoadDefault();
|
||||
ciphers = ciphers.filter(cipher => cipher.login.autofillOnPageLoad ||
|
||||
(cipher.login.autofillOnPageLoad == null && autofillOnPageLoadDefault !== false));
|
||||
if (ciphers.length === 0) {
|
||||
@@ -1128,4 +1094,17 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
return this.sortedCiphersCache.getNext(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
private async clearEncryptedCiphersState(userId?: string) {
|
||||
await this.stateService.setEncryptedCiphers(null, { userId: userId });
|
||||
}
|
||||
|
||||
private async clearDecryptedCiphersState(userId?: string) {
|
||||
await this.stateService.setDecryptedCiphers(null, { userId: userId });
|
||||
this.clearSortedCiphers();
|
||||
}
|
||||
|
||||
private clearSortedCiphers() {
|
||||
this.sortedCiphersCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,26 +8,20 @@ import { CollectionView } from '../models/view/collectionView';
|
||||
import { CollectionService as CollectionServiceAbstraction } from '../abstractions/collection.service';
|
||||
import { CryptoService } from '../abstractions/crypto.service';
|
||||
import { I18nService } from '../abstractions/i18n.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
import { ServiceUtils } from '../misc/serviceUtils';
|
||||
import { Utils } from '../misc/utils';
|
||||
|
||||
const Keys = {
|
||||
collectionsPrefix: 'collections_',
|
||||
};
|
||||
const NestingDelimiter = '/';
|
||||
|
||||
export class CollectionService implements CollectionServiceAbstraction {
|
||||
decryptedCollectionCache: CollectionView[];
|
||||
|
||||
constructor(private cryptoService: CryptoService, private userService: UserService,
|
||||
private storageService: StorageService, private i18nService: I18nService) {
|
||||
constructor(private cryptoService: CryptoService, private i18nService: I18nService,
|
||||
private stateService: StateService) {
|
||||
}
|
||||
|
||||
clearCache(): void {
|
||||
this.decryptedCollectionCache = null;
|
||||
async clearCache(userId?: string): Promise<void> {
|
||||
await this.stateService.setDecryptedCollections(null, { userId: userId });
|
||||
}
|
||||
|
||||
async encrypt(model: CollectionView): Promise<Collection> {
|
||||
@@ -60,9 +54,7 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
}
|
||||
|
||||
async get(id: string): Promise<Collection> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const collections = await this.storageService.get<{ [id: string]: CollectionData; }>(
|
||||
Keys.collectionsPrefix + userId);
|
||||
const collections = await this.stateService.getEncryptedCollections();
|
||||
if (collections == null || !collections.hasOwnProperty(id)) {
|
||||
return null;
|
||||
}
|
||||
@@ -71,9 +63,7 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
}
|
||||
|
||||
async getAll(): Promise<Collection[]> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const collections = await this.storageService.get<{ [id: string]: CollectionData; }>(
|
||||
Keys.collectionsPrefix + userId);
|
||||
const collections = await this.stateService.getEncryptedCollections();
|
||||
const response: Collection[] = [];
|
||||
for (const id in collections) {
|
||||
if (collections.hasOwnProperty(id)) {
|
||||
@@ -84,8 +74,9 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
}
|
||||
|
||||
async getAllDecrypted(): Promise<CollectionView[]> {
|
||||
if (this.decryptedCollectionCache != null) {
|
||||
return this.decryptedCollectionCache;
|
||||
let decryptedCollections = await this.stateService.getDecryptedCollections();
|
||||
if (decryptedCollections != null) {
|
||||
return decryptedCollections;
|
||||
}
|
||||
|
||||
const hasKey = await this.cryptoService.hasKey();
|
||||
@@ -94,8 +85,9 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
}
|
||||
|
||||
const collections = await this.getAll();
|
||||
this.decryptedCollectionCache = await this.decryptMany(collections);
|
||||
return this.decryptedCollectionCache;
|
||||
decryptedCollections = await this.decryptMany(collections);
|
||||
await this.stateService.setDecryptedCollections(decryptedCollections);
|
||||
return decryptedCollections;
|
||||
}
|
||||
|
||||
async getAllNested(collections: CollectionView[] = null): Promise<TreeNode<CollectionView>[]> {
|
||||
@@ -119,9 +111,7 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
}
|
||||
|
||||
async upsert(collection: CollectionData | CollectionData[]): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
let collections = await this.storageService.get<{ [id: string]: CollectionData; }>(
|
||||
Keys.collectionsPrefix + userId);
|
||||
let collections = await this.stateService.getEncryptedCollections();
|
||||
if (collections == null) {
|
||||
collections = {};
|
||||
}
|
||||
@@ -135,31 +125,26 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.collectionsPrefix + userId, collections);
|
||||
this.decryptedCollectionCache = null;
|
||||
await this.replace(collections);
|
||||
}
|
||||
|
||||
async replace(collections: { [id: string]: CollectionData; }): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
await this.storageService.save(Keys.collectionsPrefix + userId, collections);
|
||||
this.decryptedCollectionCache = null;
|
||||
await this.clearCache();
|
||||
await this.stateService.setEncryptedCollections(collections);
|
||||
}
|
||||
|
||||
async clear(userId: string): Promise<any> {
|
||||
await this.storageService.remove(Keys.collectionsPrefix + userId);
|
||||
this.decryptedCollectionCache = null;
|
||||
async clear(userId?: string): Promise<any> {
|
||||
await this.clearCache(userId);
|
||||
await this.stateService.setEncryptedCollections(null, { userId: userId });
|
||||
}
|
||||
|
||||
async delete(id: string | string[]): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const collections = await this.storageService.get<{ [id: string]: CollectionData; }>(
|
||||
Keys.collectionsPrefix + userId);
|
||||
const collections = await this.stateService.getEncryptedCollections();
|
||||
if (collections == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof id === 'string') {
|
||||
const i = id as string;
|
||||
delete collections[id];
|
||||
} else {
|
||||
(id as string[]).forEach(i => {
|
||||
@@ -167,7 +152,6 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.collectionsPrefix + userId, collections);
|
||||
this.decryptedCollectionCache = null;
|
||||
await this.replace(collections);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
export class ConstantsService {
|
||||
static readonly environmentUrlsKey: string = 'environmentUrls';
|
||||
static readonly disableGaKey: string = 'disableGa';
|
||||
static readonly disableAddLoginNotificationKey: string = 'disableAddLoginNotification';
|
||||
static readonly disableChangedPasswordNotificationKey: string = 'disableChangedPasswordNotification';
|
||||
static readonly disableContextMenuItemKey: string = 'disableContextMenuItem';
|
||||
static readonly disableFaviconKey: string = 'disableFavicon';
|
||||
static readonly disableBadgeCounterKey: string = 'disableBadgeCounter';
|
||||
static readonly disableAutoTotpCopyKey: string = 'disableAutoTotpCopy';
|
||||
static readonly disableAutoBiometricsPromptKey: string = 'noAutoPromptBiometrics';
|
||||
static readonly enableAutoFillOnPageLoadKey: string = 'enableAutoFillOnPageLoad';
|
||||
static readonly autoFillOnPageLoadDefaultKey: string = 'autoFillOnPageLoadDefault';
|
||||
static readonly vaultTimeoutKey: string = 'lockOption';
|
||||
static readonly vaultTimeoutActionKey: string = 'vaultTimeoutAction';
|
||||
static readonly lastActiveKey: string = 'lastActive';
|
||||
static readonly neverDomainsKey: string = 'neverDomains';
|
||||
static readonly installedVersionKey: string = 'installedVersion';
|
||||
static readonly localeKey: string = 'locale';
|
||||
static readonly themeKey: string = 'theme';
|
||||
static readonly collapsedGroupingsKey: string = 'collapsedGroupings';
|
||||
static readonly autoConfirmFingerprints: string = 'autoConfirmFingerprints';
|
||||
static readonly dontShowCardsCurrentTab: string = 'dontShowCardsCurrentTab';
|
||||
static readonly dontShowIdentitiesCurrentTab: string = 'dontShowIdentitiesCurrentTab';
|
||||
static readonly defaultUriMatch: string = 'defaultUriMatch';
|
||||
static readonly pinProtectedKey: string = 'pinProtectedKey';
|
||||
static readonly protectedPin: string = 'protectedPin';
|
||||
static readonly clearClipboardKey: string = 'clearClipboardKey';
|
||||
static readonly eventCollectionKey: string = 'eventCollection';
|
||||
static readonly ssoCodeVerifierKey: string = 'ssoCodeVerifier';
|
||||
static readonly ssoStateKey: string = 'ssoState';
|
||||
static readonly biometricUnlockKey: string = 'biometric';
|
||||
static readonly biometricText: string = 'biometricText';
|
||||
static readonly biometricAwaitingAcceptance: string = 'biometricAwaitingAcceptance';
|
||||
static readonly biometricFingerprintValidated: string = 'biometricFingerprintValidated';
|
||||
|
||||
readonly environmentUrlsKey: string = ConstantsService.environmentUrlsKey;
|
||||
readonly disableGaKey: string = ConstantsService.disableGaKey;
|
||||
readonly disableAddLoginNotificationKey: string = ConstantsService.disableAddLoginNotificationKey;
|
||||
readonly disableContextMenuItemKey: string = ConstantsService.disableContextMenuItemKey;
|
||||
readonly disableFaviconKey: string = ConstantsService.disableFaviconKey;
|
||||
readonly disableBadgeCounterKey: string = ConstantsService.disableBadgeCounterKey;
|
||||
readonly disableAutoTotpCopyKey: string = ConstantsService.disableAutoTotpCopyKey;
|
||||
readonly disableAutoBiometricsPromptKey: string = ConstantsService.disableAutoBiometricsPromptKey;
|
||||
readonly enableAutoFillOnPageLoadKey: string = ConstantsService.enableAutoFillOnPageLoadKey;
|
||||
readonly autoFillOnPageLoadDefaultKey: string = ConstantsService.autoFillOnPageLoadDefaultKey;
|
||||
readonly vaultTimeoutKey: string = ConstantsService.vaultTimeoutKey;
|
||||
readonly vaultTimeoutActionKey: string = ConstantsService.vaultTimeoutActionKey;
|
||||
readonly lastActiveKey: string = ConstantsService.lastActiveKey;
|
||||
readonly neverDomainsKey: string = ConstantsService.neverDomainsKey;
|
||||
readonly installedVersionKey: string = ConstantsService.installedVersionKey;
|
||||
readonly localeKey: string = ConstantsService.localeKey;
|
||||
readonly themeKey: string = ConstantsService.themeKey;
|
||||
readonly collapsedGroupingsKey: string = ConstantsService.collapsedGroupingsKey;
|
||||
readonly autoConfirmFingerprints: string = ConstantsService.autoConfirmFingerprints;
|
||||
readonly dontShowCardsCurrentTab: string = ConstantsService.dontShowCardsCurrentTab;
|
||||
readonly dontShowIdentitiesCurrentTab: string = ConstantsService.dontShowIdentitiesCurrentTab;
|
||||
readonly defaultUriMatch: string = ConstantsService.defaultUriMatch;
|
||||
readonly pinProtectedKey: string = ConstantsService.pinProtectedKey;
|
||||
readonly protectedPin: string = ConstantsService.protectedPin;
|
||||
readonly clearClipboardKey: string = ConstantsService.clearClipboardKey;
|
||||
readonly eventCollectionKey: string = ConstantsService.eventCollectionKey;
|
||||
readonly ssoCodeVerifierKey: string = ConstantsService.ssoCodeVerifierKey;
|
||||
readonly ssoStateKey: string = ConstantsService.ssoStateKey;
|
||||
readonly biometricUnlockKey: string = ConstantsService.biometricUnlockKey;
|
||||
readonly biometricText: string = ConstantsService.biometricText;
|
||||
readonly biometricAwaitingAcceptance: string = ConstantsService.biometricAwaitingAcceptance;
|
||||
readonly biometricFingerprintValidated: string = ConstantsService.biometricFingerprintValidated;
|
||||
}
|
||||
@@ -3,84 +3,60 @@ import * as bigInt from 'big-integer';
|
||||
import { EncryptionType } from '../enums/encryptionType';
|
||||
import { HashPurpose } from '../enums/hashPurpose';
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
import { KeySuffixOptions } from '../enums/keySuffixOptions';
|
||||
|
||||
import { EncArrayBuffer } from '../models/domain/encArrayBuffer';
|
||||
import { EncryptedObject } from '../models/domain/encryptedObject';
|
||||
import { EncString } from '../models/domain/encString';
|
||||
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
import { ProfileOrganizationResponse } from '../models/response/profileOrganizationResponse';
|
||||
|
||||
import { CryptoService as CryptoServiceAbstraction } from '../abstractions/crypto.service';
|
||||
import { CryptoFunctionService } from '../abstractions/cryptoFunction.service';
|
||||
import { LogService } from '../abstractions/log.service';
|
||||
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
||||
import {
|
||||
KeySuffixOptions,
|
||||
StorageService,
|
||||
} from '../abstractions/storage.service';
|
||||
|
||||
import { ConstantsService } from './constants.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
import { sequentialize } from '../misc/sequentialize';
|
||||
import { Utils } from '../misc/utils';
|
||||
import { EEFLongWordList } from '../misc/wordlist';
|
||||
|
||||
import { ProfileOrganizationResponse } from '../models/response/profileOrganizationResponse';
|
||||
import { ProfileProviderOrganizationResponse } from '../models/response/profileProviderOrganizationResponse';
|
||||
import { ProfileProviderResponse } from '../models/response/profileProviderResponse';
|
||||
|
||||
export const Keys = {
|
||||
key: 'key', // Master Key
|
||||
encOrgKeys: 'encOrgKeys',
|
||||
encProviderKeys: 'encProviderKeys',
|
||||
encPrivateKey: 'encPrivateKey',
|
||||
encKey: 'encKey', // Generated Symmetric Key
|
||||
keyHash: 'keyHash',
|
||||
};
|
||||
|
||||
export class CryptoService implements CryptoServiceAbstraction {
|
||||
private key: SymmetricCryptoKey;
|
||||
private encKey: SymmetricCryptoKey;
|
||||
private legacyEtmKey: SymmetricCryptoKey;
|
||||
private keyHash: string;
|
||||
private publicKey: ArrayBuffer;
|
||||
private privateKey: ArrayBuffer;
|
||||
private orgKeys: Map<string, SymmetricCryptoKey>;
|
||||
private providerKeys: Map<string, SymmetricCryptoKey>;
|
||||
|
||||
constructor(private storageService: StorageService, protected secureStorageService: StorageService,
|
||||
private cryptoFunctionService: CryptoFunctionService, protected platformUtilService: PlatformUtilsService,
|
||||
protected logService: LogService) {
|
||||
constructor(private cryptoFunctionService: CryptoFunctionService, protected platformUtilService: PlatformUtilsService,
|
||||
protected logService: LogService, protected stateService: StateService) {
|
||||
}
|
||||
|
||||
async setKey(key: SymmetricCryptoKey): Promise<any> {
|
||||
this.key = key;
|
||||
|
||||
await this.storeKey(key);
|
||||
async setKey(key: SymmetricCryptoKey, userId?: string): Promise<any> {
|
||||
await this.stateService.setCryptoMasterKey(key, { userId: userId });
|
||||
await this.storeKey(key, userId);
|
||||
}
|
||||
|
||||
setKeyHash(keyHash: string): Promise<{}> {
|
||||
this.keyHash = keyHash;
|
||||
return this.storageService.save(Keys.keyHash, keyHash);
|
||||
async setKeyHash(keyHash: string): Promise<void> {
|
||||
await this.stateService.setKeyHash(keyHash);
|
||||
}
|
||||
|
||||
async setEncKey(encKey: string): Promise<{}> {
|
||||
async setEncKey(encKey: string): Promise<void> {
|
||||
if (encKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.encKey, encKey);
|
||||
this.encKey = null;
|
||||
await this.stateService.setDecryptedCryptoSymmetricKey(null);
|
||||
await this.stateService.setEncryptedCryptoSymmetricKey(encKey);
|
||||
}
|
||||
|
||||
async setEncPrivateKey(encPrivateKey: string): Promise<{}> {
|
||||
async setEncPrivateKey(encPrivateKey: string): Promise<void> {
|
||||
if (encPrivateKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.encPrivateKey, encPrivateKey);
|
||||
this.privateKey = null;
|
||||
await this.stateService.setDecryptedPrivateKey(null);
|
||||
await this.stateService.setEncryptedPrivateKey(encPrivateKey);
|
||||
}
|
||||
|
||||
async setOrgKeys(orgs: ProfileOrganizationResponse[], providerOrgs: ProfileProviderOrganizationResponse[]): Promise<{}> {
|
||||
async setOrgKeys(orgs: ProfileOrganizationResponse[], providerOrgs: ProfileProviderOrganizationResponse[]): Promise<void> {
|
||||
const orgKeys: any = {};
|
||||
orgs.forEach(org => {
|
||||
orgKeys[org.id] = org.key;
|
||||
@@ -90,47 +66,49 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
// Convert provider encrypted keys to user encrypted.
|
||||
const providerKey = await this.getProviderKey(providerOrg.providerId);
|
||||
const decValue = await this.decryptToBytes(new EncString(providerOrg.key), providerKey);
|
||||
orgKeys[providerOrg.id] = await (await this.rsaEncrypt(decValue)).encryptedString;
|
||||
orgKeys[providerOrg.id] = (await this.rsaEncrypt(decValue)).encryptedString;
|
||||
}
|
||||
|
||||
this.orgKeys = null;
|
||||
return this.storageService.save(Keys.encOrgKeys, orgKeys);
|
||||
await this.stateService.setDecryptedOrganizationKeys(null);
|
||||
return await this.stateService.setEncryptedOrganizationKeys(orgKeys);
|
||||
}
|
||||
|
||||
setProviderKeys(providers: ProfileProviderResponse[]): Promise<{}> {
|
||||
async setProviderKeys(providers: ProfileProviderResponse[]): Promise<void> {
|
||||
const providerKeys: any = {};
|
||||
providers.forEach(provider => {
|
||||
providerKeys[provider.id] = provider.key;
|
||||
});
|
||||
|
||||
this.providerKeys = null;
|
||||
return this.storageService.save(Keys.encProviderKeys, providerKeys);
|
||||
await this.stateService.setDecryptedProviderKeys(null);
|
||||
return await this.stateService.setEncryptedProviderKeys(providerKeys);
|
||||
}
|
||||
|
||||
async getKey(keySuffix?: KeySuffixOptions): Promise<SymmetricCryptoKey> {
|
||||
if (this.key != null) {
|
||||
return this.key;
|
||||
async getKey(keySuffix?: KeySuffixOptions, userId?: string): Promise<SymmetricCryptoKey> {
|
||||
const inMemoryKey = await this.stateService.getCryptoMasterKey({ userId: userId });
|
||||
|
||||
if (inMemoryKey != null) {
|
||||
return inMemoryKey;
|
||||
}
|
||||
|
||||
keySuffix ||= 'auto';
|
||||
const symmetricKey = await this.getKeyFromStorage(keySuffix);
|
||||
keySuffix ||= KeySuffixOptions.Auto;
|
||||
const symmetricKey = await this.getKeyFromStorage(keySuffix, userId);
|
||||
|
||||
if (symmetricKey != null) {
|
||||
this.setKey(symmetricKey);
|
||||
// TODO: Refactor here so get key doesn't also set key
|
||||
this.setKey(symmetricKey, userId);
|
||||
}
|
||||
|
||||
return symmetricKey;
|
||||
}
|
||||
|
||||
async getKeyFromStorage(keySuffix: KeySuffixOptions): Promise<SymmetricCryptoKey> {
|
||||
const key = await this.retrieveKeyFromStorage(keySuffix);
|
||||
async getKeyFromStorage(keySuffix: KeySuffixOptions, userId?: string): Promise<SymmetricCryptoKey> {
|
||||
const key = await this.retrieveKeyFromStorage(keySuffix, userId);
|
||||
if (key != null) {
|
||||
|
||||
const symmetricKey = new SymmetricCryptoKey(Utils.fromB64ToArray(key).buffer);
|
||||
|
||||
if (!await this.validateKey(symmetricKey)) {
|
||||
this.logService.warning('Wrong key, throwing away stored key');
|
||||
this.secureStorageService.remove(Keys.key, { keySuffix: keySuffix });
|
||||
await this.clearSecretKeyStore(userId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -140,16 +118,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
async getKeyHash(): Promise<string> {
|
||||
if (this.keyHash != null) {
|
||||
return this.keyHash;
|
||||
}
|
||||
|
||||
const keyHash = await this.storageService.get<string>(Keys.keyHash);
|
||||
if (keyHash != null) {
|
||||
this.keyHash = keyHash;
|
||||
}
|
||||
|
||||
return keyHash == null ? null : this.keyHash;
|
||||
return await this.stateService.getKeyHash();
|
||||
}
|
||||
|
||||
async compareAndUpdateKeyHash(masterPassword: string, key: SymmetricCryptoKey): Promise<boolean> {
|
||||
@@ -173,11 +142,12 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
|
||||
@sequentialize(() => 'getEncKey')
|
||||
async getEncKey(key: SymmetricCryptoKey = null): Promise<SymmetricCryptoKey> {
|
||||
if (this.encKey != null) {
|
||||
return this.encKey;
|
||||
const inMemoryKey = await this.stateService.getDecryptedCryptoSymmetricKey();
|
||||
if (inMemoryKey != null) {
|
||||
return inMemoryKey;
|
||||
}
|
||||
|
||||
const encKey = await this.storageService.get<string>(Keys.encKey);
|
||||
const encKey = await this.stateService.getEncryptedCryptoSymmetricKey();
|
||||
if (encKey == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -203,13 +173,15 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
if (decEncKey == null) {
|
||||
return null;
|
||||
}
|
||||
this.encKey = new SymmetricCryptoKey(decEncKey);
|
||||
return this.encKey;
|
||||
const symmetricCryptoKey = new SymmetricCryptoKey(decEncKey);
|
||||
await this.stateService.setDecryptedCryptoSymmetricKey(symmetricCryptoKey);
|
||||
return symmetricCryptoKey;
|
||||
}
|
||||
|
||||
async getPublicKey(): Promise<ArrayBuffer> {
|
||||
if (this.publicKey != null) {
|
||||
return this.publicKey;
|
||||
const inMemoryPublicKey = await this.stateService.getPublicKey();
|
||||
if (inMemoryPublicKey != null) {
|
||||
return inMemoryPublicKey;
|
||||
}
|
||||
|
||||
const privateKey = await this.getPrivateKey();
|
||||
@@ -217,22 +189,25 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.publicKey = await this.cryptoFunctionService.rsaExtractPublicKey(privateKey);
|
||||
return this.publicKey;
|
||||
const publicKey = await this.cryptoFunctionService.rsaExtractPublicKey(privateKey);
|
||||
await this.stateService.setPublicKey(publicKey);
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
async getPrivateKey(): Promise<ArrayBuffer> {
|
||||
if (this.privateKey != null) {
|
||||
return this.privateKey;
|
||||
const decryptedPrivateKey = await this.stateService.getDecryptedPrivateKey();
|
||||
if (decryptedPrivateKey != null) {
|
||||
return decryptedPrivateKey;
|
||||
}
|
||||
|
||||
const encPrivateKey = await this.storageService.get<string>(Keys.encPrivateKey);
|
||||
const encPrivateKey = await this.stateService.getEncryptedPrivateKey();
|
||||
if (encPrivateKey == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.privateKey = await this.decryptToBytes(new EncString(encPrivateKey), null);
|
||||
return this.privateKey;
|
||||
const privateKey = await this.decryptToBytes(new EncString(encPrivateKey), null);
|
||||
await this.stateService.setDecryptedPrivateKey(privateKey);
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
async getFingerprint(userId: string, publicKey?: ArrayBuffer): Promise<string[]> {
|
||||
@@ -249,16 +224,17 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
|
||||
@sequentialize(() => 'getOrgKeys')
|
||||
async getOrgKeys(): Promise<Map<string, SymmetricCryptoKey>> {
|
||||
if (this.orgKeys != null && this.orgKeys.size > 0) {
|
||||
return this.orgKeys;
|
||||
const orgKeys: Map<string, SymmetricCryptoKey> = new Map<string, SymmetricCryptoKey>();
|
||||
const decryptedOrganizationKeys = await this.stateService.getDecryptedOrganizationKeys();
|
||||
if (decryptedOrganizationKeys != null && decryptedOrganizationKeys.size > 0) {
|
||||
return decryptedOrganizationKeys;
|
||||
}
|
||||
|
||||
const encOrgKeys = await this.storageService.get<any>(Keys.encOrgKeys);
|
||||
const encOrgKeys = await this.stateService.getEncryptedOrganizationKeys();
|
||||
if (encOrgKeys == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const orgKeys: Map<string, SymmetricCryptoKey> = new Map<string, SymmetricCryptoKey>();
|
||||
let setKey = false;
|
||||
|
||||
for (const orgId in encOrgKeys) {
|
||||
@@ -272,10 +248,10 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
if (setKey) {
|
||||
this.orgKeys = orgKeys;
|
||||
await this.stateService.setDecryptedOrganizationKeys(orgKeys);
|
||||
}
|
||||
|
||||
return this.orgKeys;
|
||||
return orgKeys;
|
||||
}
|
||||
|
||||
async getOrgKey(orgId: string): Promise<SymmetricCryptoKey> {
|
||||
@@ -293,16 +269,17 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
|
||||
@sequentialize(() => 'getProviderKeys')
|
||||
async getProviderKeys(): Promise<Map<string, SymmetricCryptoKey>> {
|
||||
if (this.providerKeys != null && this.providerKeys.size > 0) {
|
||||
return this.providerKeys;
|
||||
const providerKeys: Map<string, SymmetricCryptoKey> = new Map<string, SymmetricCryptoKey>();
|
||||
const decryptedProviderKeys = await this.stateService.getDecryptedProviderKeys();
|
||||
if (decryptedProviderKeys != null && decryptedProviderKeys.size > 0) {
|
||||
return decryptedProviderKeys;
|
||||
}
|
||||
|
||||
const encProviderKeys = await this.storageService.get<any>(Keys.encProviderKeys);
|
||||
const encProviderKeys = await this.stateService.getEncryptedProviderKeys();
|
||||
if (encProviderKeys == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const providerKeys: Map<string, SymmetricCryptoKey> = new Map<string, SymmetricCryptoKey>();
|
||||
let setKey = false;
|
||||
|
||||
for (const orgId in encProviderKeys) {
|
||||
@@ -316,10 +293,10 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
if (setKey) {
|
||||
this.providerKeys = providerKeys;
|
||||
await this.stateService.setDecryptedProviderKeys(providerKeys);
|
||||
}
|
||||
|
||||
return this.providerKeys;
|
||||
return providerKeys;
|
||||
}
|
||||
|
||||
async getProviderKey(providerId: string): Promise<SymmetricCryptoKey> {
|
||||
@@ -336,84 +313,87 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
async hasKey(): Promise<boolean> {
|
||||
return this.hasKeyInMemory() || await this.hasKeyStored('auto') || await this.hasKeyStored('biometric');
|
||||
return await this.hasKeyInMemory() || await this.hasKeyStored(KeySuffixOptions.Auto) || await this.hasKeyStored(KeySuffixOptions.Biometric);
|
||||
}
|
||||
|
||||
hasKeyInMemory(): boolean {
|
||||
return this.key != null;
|
||||
async hasKeyInMemory(userId?: string): Promise<boolean> {
|
||||
return await this.stateService.getCryptoMasterKey({ userId: userId }) != null;
|
||||
}
|
||||
|
||||
hasKeyStored(keySuffix: KeySuffixOptions): Promise<boolean> {
|
||||
return this.secureStorageService.has(Keys.key, { keySuffix: keySuffix });
|
||||
async hasKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise<boolean> {
|
||||
const key = keySuffix === KeySuffixOptions.Auto ?
|
||||
await this.stateService.getCryptoMasterKeyAuto({ userId: userId }) :
|
||||
await this.stateService.hasCryptoMasterKeyBiometric({ userId: userId });
|
||||
|
||||
return key != null;
|
||||
}
|
||||
|
||||
async hasEncKey(): Promise<boolean> {
|
||||
const encKey = await this.storageService.get<string>(Keys.encKey);
|
||||
return encKey != null;
|
||||
return await this.stateService.getEncryptedCryptoSymmetricKey() != null;
|
||||
}
|
||||
|
||||
async clearKey(clearSecretStorage: boolean = true): Promise<any> {
|
||||
this.key = this.legacyEtmKey = null;
|
||||
async clearKey(clearSecretStorage: boolean = true, userId?: string): Promise<any> {
|
||||
await this.stateService.setCryptoMasterKey(null, { userId: userId });
|
||||
await this.stateService.setLegacyEtmKey(null, { userId: userId });
|
||||
if (clearSecretStorage) {
|
||||
this.clearStoredKey('auto');
|
||||
this.clearStoredKey('biometric');
|
||||
await this.clearSecretKeyStore(userId);
|
||||
}
|
||||
}
|
||||
|
||||
async clearStoredKey(keySuffix: KeySuffixOptions) {
|
||||
await this.secureStorageService.remove(Keys.key, { keySuffix: keySuffix });
|
||||
keySuffix === KeySuffixOptions.Auto ?
|
||||
await this.stateService.setCryptoMasterKeyAuto(null) :
|
||||
await this.stateService.setCryptoMasterKeyBiometric(null);
|
||||
}
|
||||
|
||||
clearKeyHash(): Promise<any> {
|
||||
this.keyHash = null;
|
||||
return this.storageService.remove(Keys.keyHash);
|
||||
async clearKeyHash(userId?: string): Promise<any> {
|
||||
return await this.stateService.setKeyHash(null, { userId: userId });
|
||||
}
|
||||
|
||||
clearEncKey(memoryOnly?: boolean): Promise<any> {
|
||||
this.encKey = null;
|
||||
if (memoryOnly) {
|
||||
return Promise.resolve();
|
||||
async clearEncKey(memoryOnly?: boolean, userId?: string): Promise<void> {
|
||||
await this.stateService.setDecryptedCryptoSymmetricKey(null, { userId: userId });
|
||||
if (!memoryOnly) {
|
||||
await this.stateService.setEncryptedCryptoSymmetricKey(null, { userId: userId });
|
||||
}
|
||||
return this.storageService.remove(Keys.encKey);
|
||||
}
|
||||
|
||||
clearKeyPair(memoryOnly?: boolean): Promise<any> {
|
||||
this.privateKey = null;
|
||||
this.publicKey = null;
|
||||
if (memoryOnly) {
|
||||
return Promise.resolve();
|
||||
async clearKeyPair(memoryOnly?: boolean, userId?: string): Promise<any> {
|
||||
const keysToClear: Promise<void>[] = [
|
||||
this.stateService.setDecryptedPrivateKey(null, { userId: userId }),
|
||||
this.stateService.setPublicKey(null, { userId: userId }),
|
||||
];
|
||||
if (!memoryOnly) {
|
||||
keysToClear.push(this.stateService.setEncryptedPrivateKey(null, { userId: userId }));
|
||||
}
|
||||
return this.storageService.remove(Keys.encPrivateKey);
|
||||
return Promise.all(keysToClear);
|
||||
}
|
||||
|
||||
clearOrgKeys(memoryOnly?: boolean): Promise<any> {
|
||||
this.orgKeys = null;
|
||||
if (memoryOnly) {
|
||||
return Promise.resolve();
|
||||
async clearOrgKeys(memoryOnly?: boolean, userId?: string): Promise<void> {
|
||||
await this.stateService.setDecryptedOrganizationKeys(null, { userId: userId });
|
||||
if (!memoryOnly) {
|
||||
await this.stateService.setEncryptedOrganizationKeys(null, { userId: userId });
|
||||
}
|
||||
}
|
||||
|
||||
async clearProviderKeys(memoryOnly?: boolean, userId?: string): Promise<void> {
|
||||
await this.stateService.setDecryptedProviderKeys(null, { userId: userId });
|
||||
if (!memoryOnly) {
|
||||
await this.stateService.setEncryptedProviderKeys(null, { userId: userId });
|
||||
}
|
||||
return this.storageService.remove(Keys.encOrgKeys);
|
||||
}
|
||||
|
||||
clearProviderKeys(memoryOnly?: boolean): Promise<any> {
|
||||
this.providerKeys = null;
|
||||
if (memoryOnly) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this.storageService.remove(Keys.encOrgKeys);
|
||||
async clearPinProtectedKey(userId?: string): Promise<any> {
|
||||
return await this.stateService.setEncryptedPinProtected(null, { userId: userId });
|
||||
}
|
||||
|
||||
clearPinProtectedKey(): Promise<any> {
|
||||
return this.storageService.remove(ConstantsService.pinProtectedKey);
|
||||
}
|
||||
|
||||
async clearKeys(): Promise<any> {
|
||||
await this.clearKey();
|
||||
await this.clearKeyHash();
|
||||
await this.clearOrgKeys();
|
||||
await this.clearProviderKeys();
|
||||
await this.clearEncKey();
|
||||
await this.clearKeyPair();
|
||||
await this.clearPinProtectedKey();
|
||||
async clearKeys(userId?: string): Promise<any> {
|
||||
await this.clearKey(true, userId);
|
||||
await this.clearKeyHash(userId);
|
||||
await this.clearOrgKeys(false, userId);
|
||||
await this.clearProviderKeys(false, userId);
|
||||
await this.clearEncKey(false, userId);
|
||||
await this.clearKeyPair(false, userId);
|
||||
await this.clearPinProtectedKey(userId);
|
||||
}
|
||||
|
||||
async toggleKey(): Promise<any> {
|
||||
@@ -442,7 +422,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
protectedKeyCs: EncString = null):
|
||||
Promise<SymmetricCryptoKey> {
|
||||
if (protectedKeyCs == null) {
|
||||
const pinProtectedKey = await this.storageService.get<string>(ConstantsService.pinProtectedKey);
|
||||
const pinProtectedKey = await this.stateService.getEncryptedPinProtected();
|
||||
if (pinProtectedKey == null) {
|
||||
throw new Error('No PIN protected key found.');
|
||||
}
|
||||
@@ -699,7 +679,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
|
||||
async validateKey(key: SymmetricCryptoKey) {
|
||||
try {
|
||||
const encPrivateKey = await this.storageService.get<string>(Keys.encPrivateKey);
|
||||
const encPrivateKey = await this.stateService.getEncryptedPrivateKey();
|
||||
const encKey = await this.getEncKey(key);
|
||||
if (encPrivateKey == null || encKey == null) {
|
||||
return false;
|
||||
@@ -715,29 +695,30 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
protected async storeKey(key: SymmetricCryptoKey) {
|
||||
if (await this.shouldStoreKey('auto') || await this.shouldStoreKey('biometric')) {
|
||||
this.secureStorageService.save(Keys.key, key.keyB64);
|
||||
protected async storeKey(key: SymmetricCryptoKey, userId?: string) {
|
||||
if (await this.shouldStoreKey(KeySuffixOptions.Auto, userId) || await this.shouldStoreKey(KeySuffixOptions.Biometric, userId)) {
|
||||
await this.stateService.setCryptoMasterKeyB64(key.keyB64, { userId: userId });
|
||||
} else {
|
||||
this.secureStorageService.remove(Keys.key);
|
||||
await this.stateService.setCryptoMasterKeyB64(null, { userId: userId });
|
||||
}
|
||||
}
|
||||
|
||||
protected async shouldStoreKey(keySuffix: KeySuffixOptions) {
|
||||
protected async shouldStoreKey(keySuffix: KeySuffixOptions, userId?: string) {
|
||||
let shouldStoreKey = false;
|
||||
if (keySuffix === 'auto') {
|
||||
const vaultTimeout = await this.storageService.get<number>(ConstantsService.vaultTimeoutKey);
|
||||
if (keySuffix === KeySuffixOptions.Auto) {
|
||||
const vaultTimeout = await this.stateService.getVaultTimeout({ userId: userId });
|
||||
shouldStoreKey = vaultTimeout == null;
|
||||
} else if (keySuffix === 'biometric') {
|
||||
const biometricUnlock = await this.storageService.get<boolean>(ConstantsService.biometricUnlockKey);
|
||||
} else if (keySuffix === KeySuffixOptions.Biometric) {
|
||||
const biometricUnlock = await this.stateService.getBiometricUnlock({ userId: userId });
|
||||
shouldStoreKey = biometricUnlock && this.platformUtilService.supportsSecureStorage();
|
||||
}
|
||||
return shouldStoreKey;
|
||||
}
|
||||
|
||||
protected retrieveKeyFromStorage(keySuffix: KeySuffixOptions) {
|
||||
return this.secureStorageService.get<string>(Keys.key, { keySuffix: keySuffix });
|
||||
protected async retrieveKeyFromStorage(keySuffix: KeySuffixOptions, userId?: string) {
|
||||
return keySuffix === KeySuffixOptions.Auto ?
|
||||
await this.stateService.getCryptoMasterKeyAuto({ userId: userId }) :
|
||||
await this.stateService.getCryptoMasterKeyBiometric({ userId: userId });
|
||||
}
|
||||
|
||||
private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise<EncryptedObject> {
|
||||
@@ -759,7 +740,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
private async aesDecryptToUtf8(encType: EncryptionType, data: string, iv: string, mac: string,
|
||||
key: SymmetricCryptoKey): Promise<string> {
|
||||
const keyForEnc = await this.getKeyForEncryption(key);
|
||||
const theKey = this.resolveLegacyKey(encType, keyForEnc);
|
||||
const theKey = await this.resolveLegacyKey(encType, keyForEnc);
|
||||
|
||||
if (theKey.macKey != null && mac == null) {
|
||||
this.logService.error('mac required.');
|
||||
@@ -788,7 +769,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
private async aesDecryptToBytes(encType: EncryptionType, data: ArrayBuffer, iv: ArrayBuffer,
|
||||
mac: ArrayBuffer, key: SymmetricCryptoKey): Promise<ArrayBuffer> {
|
||||
const keyForEnc = await this.getKeyForEncryption(key);
|
||||
const theKey = this.resolveLegacyKey(encType, keyForEnc);
|
||||
const theKey = await this.resolveLegacyKey(encType, keyForEnc);
|
||||
|
||||
if (theKey.macKey != null && mac == null) {
|
||||
return null;
|
||||
@@ -830,14 +811,16 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return await this.getKey();
|
||||
}
|
||||
|
||||
private resolveLegacyKey(encType: EncryptionType, key: SymmetricCryptoKey): SymmetricCryptoKey {
|
||||
private async resolveLegacyKey(encType: EncryptionType, key: SymmetricCryptoKey): Promise<SymmetricCryptoKey> {
|
||||
if (encType === EncryptionType.AesCbc128_HmacSha256_B64 &&
|
||||
key.encType === EncryptionType.AesCbc256_B64) {
|
||||
// Old encrypt-then-mac scheme, make a new key
|
||||
if (this.legacyEtmKey == null) {
|
||||
this.legacyEtmKey = new SymmetricCryptoKey(key.key, EncryptionType.AesCbc128_HmacSha256_B64);
|
||||
let legacyKey = await this.stateService.getLegacyEtmKey();
|
||||
if (legacyKey == null) {
|
||||
legacyKey = new SymmetricCryptoKey(key.key, EncryptionType.AesCbc128_HmacSha256_B64);
|
||||
await this.stateService.setLegacyEtmKey(legacyKey);
|
||||
}
|
||||
return this.legacyEtmKey;
|
||||
return legacyKey;
|
||||
}
|
||||
|
||||
return key;
|
||||
@@ -885,4 +868,9 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
return [new SymmetricCryptoKey(encKey), encKeyEnc];
|
||||
}
|
||||
|
||||
private async clearSecretKeyStore(userId?: string): Promise<void> {
|
||||
await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId });
|
||||
await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,8 @@ import { Observable, Subject } from 'rxjs';
|
||||
|
||||
import { EnvironmentUrls } from '../models/domain/environmentUrls';
|
||||
|
||||
import { ConstantsService } from './constants.service';
|
||||
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction, Urls } from '../abstractions/environment.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
|
||||
@@ -21,7 +19,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
private eventsUrl: string;
|
||||
private keyConnectorUrl: string;
|
||||
|
||||
constructor(private storageService: StorageService) {}
|
||||
constructor(private stateService: StateService) {}
|
||||
|
||||
hasBaseUrl() {
|
||||
return this.baseUrl != null;
|
||||
@@ -109,7 +107,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
}
|
||||
|
||||
async setUrlsFromStorage(): Promise<void> {
|
||||
const urlsObj: any = await this.storageService.get(ConstantsService.environmentUrlsKey);
|
||||
const urlsObj: any = await this.stateService.getEnvironmentUrls();
|
||||
const urls = urlsObj || {
|
||||
base: null,
|
||||
api: null,
|
||||
@@ -148,7 +146,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||
urls.keyConnector = this.formatUrl(urls.keyConnector);
|
||||
|
||||
if (saveSettings) {
|
||||
await this.storageService.save(ConstantsService.environmentUrlsKey, {
|
||||
await this.stateService.setEnvironmentUrls({
|
||||
base: urls.base,
|
||||
api: urls.api,
|
||||
identity: urls.identity,
|
||||
|
||||
@@ -7,18 +7,16 @@ import { EventRequest } from '../models/request/eventRequest';
|
||||
import { ApiService } from '../abstractions/api.service';
|
||||
import { CipherService } from '../abstractions/cipher.service';
|
||||
import { EventService as EventServiceAbstraction } from '../abstractions/event.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
|
||||
import { LogService } from '../abstractions/log.service';
|
||||
import { ConstantsService } from './constants.service';
|
||||
import { OrganizationService } from '../abstractions/organization.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
export class EventService implements EventServiceAbstraction {
|
||||
private inited = false;
|
||||
|
||||
constructor(private storageService: StorageService, private apiService: ApiService,
|
||||
private userService: UserService, private cipherService: CipherService,
|
||||
private logService: LogService) { }
|
||||
constructor(private apiService: ApiService, private cipherService: CipherService,
|
||||
private stateService: StateService, private logService: LogService,
|
||||
private organizationService: OrganizationService) { }
|
||||
|
||||
init(checkOnInterval: boolean) {
|
||||
if (this.inited) {
|
||||
@@ -33,11 +31,11 @@ export class EventService implements EventServiceAbstraction {
|
||||
}
|
||||
|
||||
async collect(eventType: EventType, cipherId: string = null, uploadImmediately = false): Promise<any> {
|
||||
const authed = await this.userService.isAuthenticated();
|
||||
const authed = await this.stateService.getIsAuthenticated();
|
||||
if (!authed) {
|
||||
return;
|
||||
}
|
||||
const organizations = await this.userService.getAllOrganizations();
|
||||
const organizations = await this.organizationService.getAll();
|
||||
if (organizations == null) {
|
||||
return;
|
||||
}
|
||||
@@ -51,7 +49,7 @@ export class EventService implements EventServiceAbstraction {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let eventCollection = await this.storageService.get<EventData[]>(ConstantsService.eventCollectionKey);
|
||||
let eventCollection = await this.stateService.getEventCollection();
|
||||
if (eventCollection == null) {
|
||||
eventCollection = [];
|
||||
}
|
||||
@@ -60,18 +58,18 @@ export class EventService implements EventServiceAbstraction {
|
||||
event.cipherId = cipherId;
|
||||
event.date = new Date().toISOString();
|
||||
eventCollection.push(event);
|
||||
await this.storageService.save(ConstantsService.eventCollectionKey, eventCollection);
|
||||
await this.stateService.setEventCollection(eventCollection);
|
||||
if (uploadImmediately) {
|
||||
await this.uploadEvents();
|
||||
}
|
||||
}
|
||||
|
||||
async uploadEvents(): Promise<any> {
|
||||
const authed = await this.userService.isAuthenticated();
|
||||
async uploadEvents(userId?: string): Promise<any> {
|
||||
const authed = await this.stateService.getIsAuthenticated({ userId: userId });
|
||||
if (!authed) {
|
||||
return;
|
||||
}
|
||||
const eventCollection = await this.storageService.get<EventData[]>(ConstantsService.eventCollectionKey);
|
||||
const eventCollection = await this.stateService.getEventCollection({ userId: userId });
|
||||
if (eventCollection == null || eventCollection.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -84,13 +82,13 @@ export class EventService implements EventServiceAbstraction {
|
||||
});
|
||||
try {
|
||||
await this.apiService.postEventsCollect(request);
|
||||
this.clearEvents();
|
||||
this.clearEvents(userId);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async clearEvents(): Promise<any> {
|
||||
await this.storageService.remove(ConstantsService.eventCollectionKey);
|
||||
async clearEvents(userId?: string): Promise<any> {
|
||||
await this.stateService.setEventCollection(null, { userId: userId });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,28 +15,22 @@ import { CipherService } from '../abstractions/cipher.service';
|
||||
import { CryptoService } from '../abstractions/crypto.service';
|
||||
import { FolderService as FolderServiceAbstraction } from '../abstractions/folder.service';
|
||||
import { I18nService } from '../abstractions/i18n.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
import { CipherData } from '../models/data/cipherData';
|
||||
|
||||
import { ServiceUtils } from '../misc/serviceUtils';
|
||||
import { Utils } from '../misc/utils';
|
||||
|
||||
const Keys = {
|
||||
foldersPrefix: 'folders_',
|
||||
ciphersPrefix: 'ciphers_',
|
||||
};
|
||||
const NestingDelimiter = '/';
|
||||
|
||||
export class FolderService implements FolderServiceAbstraction {
|
||||
decryptedFolderCache: FolderView[];
|
||||
constructor(private cryptoService: CryptoService, private apiService: ApiService,
|
||||
private i18nService: I18nService, private cipherService: CipherService,
|
||||
private stateService: StateService) { }
|
||||
|
||||
constructor(private cryptoService: CryptoService, private userService: UserService,
|
||||
private apiService: ApiService, private storageService: StorageService,
|
||||
private i18nService: I18nService, private cipherService: CipherService) { }
|
||||
|
||||
clearCache(): void {
|
||||
this.decryptedFolderCache = null;
|
||||
async clearCache(userId?: string): Promise<void> {
|
||||
await this.stateService.setDecryptedFolders(null, { userId: userId });
|
||||
}
|
||||
|
||||
async encrypt(model: FolderView, key?: SymmetricCryptoKey): Promise<Folder> {
|
||||
@@ -47,9 +41,7 @@ export class FolderService implements FolderServiceAbstraction {
|
||||
}
|
||||
|
||||
async get(id: string): Promise<Folder> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const folders = await this.storageService.get<{ [id: string]: FolderData; }>(
|
||||
Keys.foldersPrefix + userId);
|
||||
const folders = await this.stateService.getEncryptedFolders();
|
||||
if (folders == null || !folders.hasOwnProperty(id)) {
|
||||
return null;
|
||||
}
|
||||
@@ -58,9 +50,7 @@ export class FolderService implements FolderServiceAbstraction {
|
||||
}
|
||||
|
||||
async getAll(): Promise<Folder[]> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const folders = await this.storageService.get<{ [id: string]: FolderData; }>(
|
||||
Keys.foldersPrefix + userId);
|
||||
const folders = await this.stateService.getEncryptedFolders();
|
||||
const response: Folder[] = [];
|
||||
for (const id in folders) {
|
||||
if (folders.hasOwnProperty(id)) {
|
||||
@@ -71,8 +61,9 @@ export class FolderService implements FolderServiceAbstraction {
|
||||
}
|
||||
|
||||
async getAllDecrypted(): Promise<FolderView[]> {
|
||||
if (this.decryptedFolderCache != null) {
|
||||
return this.decryptedFolderCache;
|
||||
const decryptedFolders = await this.stateService.getDecryptedFolders();
|
||||
if (decryptedFolders != null) {
|
||||
return decryptedFolders;
|
||||
}
|
||||
|
||||
const hasKey = await this.cryptoService.hasKey();
|
||||
@@ -94,8 +85,8 @@ export class FolderService implements FolderServiceAbstraction {
|
||||
noneFolder.name = this.i18nService.t('noneFolder');
|
||||
decFolders.push(noneFolder);
|
||||
|
||||
this.decryptedFolderCache = decFolders;
|
||||
return this.decryptedFolderCache;
|
||||
await this.stateService.setDecryptedFolders(decFolders);
|
||||
return decFolders;
|
||||
}
|
||||
|
||||
async getAllNested(): Promise<TreeNode<FolderView>[]> {
|
||||
@@ -127,15 +118,13 @@ export class FolderService implements FolderServiceAbstraction {
|
||||
response = await this.apiService.putFolder(folder.id, request);
|
||||
}
|
||||
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
const data = new FolderData(response, userId);
|
||||
await this.upsert(data);
|
||||
}
|
||||
|
||||
async upsert(folder: FolderData | FolderData[]): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
let folders = await this.storageService.get<{ [id: string]: FolderData; }>(
|
||||
Keys.foldersPrefix + userId);
|
||||
let folders = await this.stateService.getEncryptedFolders();
|
||||
if (folders == null) {
|
||||
folders = {};
|
||||
}
|
||||
@@ -149,25 +138,22 @@ export class FolderService implements FolderServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.foldersPrefix + userId, folders);
|
||||
this.decryptedFolderCache = null;
|
||||
await this.stateService.setDecryptedFolders(null);
|
||||
await this.stateService.setEncryptedFolders(folders);
|
||||
}
|
||||
|
||||
async replace(folders: { [id: string]: FolderData; }): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
await this.storageService.save(Keys.foldersPrefix + userId, folders);
|
||||
this.decryptedFolderCache = null;
|
||||
await this.stateService.setDecryptedFolders(null);
|
||||
await this.stateService.setEncryptedFolders(folders);
|
||||
}
|
||||
|
||||
async clear(userId: string): Promise<any> {
|
||||
await this.storageService.remove(Keys.foldersPrefix + userId);
|
||||
this.decryptedFolderCache = null;
|
||||
async clear(userId?: string): Promise<any> {
|
||||
await this.stateService.setDecryptedFolders(null, { userId: userId });
|
||||
await this.stateService.setEncryptedFolders(null, { userId: userId });
|
||||
}
|
||||
|
||||
async delete(id: string | string[]): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const folders = await this.storageService.get<{ [id: string]: FolderData; }>(
|
||||
Keys.foldersPrefix + userId);
|
||||
const folders = await this.stateService.getEncryptedFolders();
|
||||
if (folders == null) {
|
||||
return;
|
||||
}
|
||||
@@ -183,11 +169,11 @@ export class FolderService implements FolderServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.foldersPrefix + userId, folders);
|
||||
this.decryptedFolderCache = null;
|
||||
await this.stateService.setDecryptedFolders(null);
|
||||
await this.stateService.setEncryptedFolders(folders);
|
||||
|
||||
// Items in a deleted folder are re-assigned to "No Folder"
|
||||
const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(Keys.ciphersPrefix + userId);
|
||||
const ciphers = await this.stateService.getEncryptedCiphers();
|
||||
if (ciphers != null) {
|
||||
const updates: CipherData[] = [];
|
||||
for (const cId in ciphers) {
|
||||
|
||||
@@ -2,9 +2,9 @@ import { ApiService } from '../abstractions/api.service';
|
||||
import { CryptoService } from '../abstractions/crypto.service';
|
||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from '../abstractions/keyConnector.service';
|
||||
import { LogService } from '../abstractions/log.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { OrganizationService } from '../abstractions/organization.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
import { TokenService } from '../abstractions/token.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
|
||||
import { OrganizationUserType } from '../enums/organizationUserType';
|
||||
|
||||
@@ -14,25 +14,17 @@ import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
|
||||
import { KeyConnectorUserKeyRequest } from '../models/request/keyConnectorUserKeyRequest';
|
||||
|
||||
const Keys = {
|
||||
usesKeyConnector: 'usesKeyConnector',
|
||||
convertAccountToKeyConnector: 'convertAccountToKeyConnector',
|
||||
};
|
||||
|
||||
export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
||||
private usesKeyConnector?: boolean = null;
|
||||
|
||||
constructor(private storageService: StorageService, private userService: UserService,
|
||||
private cryptoService: CryptoService, private apiService: ApiService,
|
||||
private tokenService: TokenService, private logService: LogService) { }
|
||||
constructor(private stateService: StateService, private cryptoService: CryptoService,
|
||||
private apiService: ApiService, private tokenService: TokenService,
|
||||
private logService: LogService, private organizationService: OrganizationService) { }
|
||||
|
||||
setUsesKeyConnector(usesKeyConnector: boolean) {
|
||||
this.usesKeyConnector = usesKeyConnector;
|
||||
return this.storageService.save(Keys.usesKeyConnector, usesKeyConnector);
|
||||
return this.stateService.setUsesKeyConnector(usesKeyConnector);
|
||||
}
|
||||
|
||||
async getUsesKeyConnector(): Promise<boolean> {
|
||||
return this.usesKeyConnector ??= await this.storageService.get<boolean>(Keys.usesKeyConnector);
|
||||
return await this.stateService.getUsesKeyConnector();
|
||||
}
|
||||
|
||||
async userNeedsMigration() {
|
||||
@@ -70,7 +62,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
||||
}
|
||||
|
||||
async getManagingOrganization() {
|
||||
const orgs = await this.userService.getAllOrganizations();
|
||||
const orgs = await this.organizationService.getAll();
|
||||
return orgs.find(o =>
|
||||
o.keyConnectorEnabled &&
|
||||
o.type !== OrganizationUserType.Admin &&
|
||||
@@ -79,15 +71,15 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
||||
}
|
||||
|
||||
async setConvertAccountRequired(status: boolean) {
|
||||
await this.storageService.save(Keys.convertAccountToKeyConnector, status);
|
||||
await this.stateService.setConvertAccountToKeyConnector(status);
|
||||
}
|
||||
|
||||
async getConvertAccountRequired(): Promise<boolean> {
|
||||
return await this.storageService.get(Keys.convertAccountToKeyConnector);
|
||||
return await this.stateService.getConvertAccountToKeyConnector();
|
||||
}
|
||||
|
||||
async removeConvertAccountRequired() {
|
||||
await this.storageService.remove(Keys.convertAccountToKeyConnector);
|
||||
await this.stateService.setConvertAccountToKeyConnector(null);
|
||||
}
|
||||
|
||||
async clear() {
|
||||
|
||||
@@ -8,8 +8,8 @@ import { AppIdService } from '../abstractions/appId.service';
|
||||
import { EnvironmentService } from '../abstractions/environment.service';
|
||||
import { LogService } from '../abstractions/log.service';
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from '../abstractions/notifications.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
import { SyncService } from '../abstractions/sync.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
import { VaultTimeoutService } from '../abstractions/vaultTimeout.service';
|
||||
|
||||
import {
|
||||
@@ -27,10 +27,10 @@ export class NotificationsService implements NotificationsServiceAbstraction {
|
||||
private inactive = false;
|
||||
private reconnectTimer: any = null;
|
||||
|
||||
constructor(private userService: UserService, private syncService: SyncService,
|
||||
private appIdService: AppIdService, private apiService: ApiService,
|
||||
private vaultTimeoutService: VaultTimeoutService, private environmentService: EnvironmentService,
|
||||
private logoutCallback: () => Promise<void>, private logService: LogService) {
|
||||
constructor(private syncService: SyncService, private appIdService: AppIdService,
|
||||
private apiService: ApiService, private vaultTimeoutService: VaultTimeoutService,
|
||||
private environmentService: EnvironmentService, private logoutCallback: () => Promise<void>,
|
||||
private logService: LogService, private stateService: StateService) {
|
||||
this.environmentService.urls.subscribe(() => {
|
||||
if (!this.inited) {
|
||||
return;
|
||||
@@ -117,9 +117,9 @@ export class NotificationsService implements NotificationsServiceAbstraction {
|
||||
return;
|
||||
}
|
||||
|
||||
const isAuthenticated = await this.userService.isAuthenticated();
|
||||
const isAuthenticated = await this.stateService.getIsAuthenticated();
|
||||
const payloadUserId = notification.payload.userId || notification.payload.UserId;
|
||||
const myUserId = await this.userService.getUserId();
|
||||
const myUserId = await this.stateService.getUserId();
|
||||
if (isAuthenticated && payloadUserId != null && payloadUserId !== myUserId) {
|
||||
return;
|
||||
}
|
||||
@@ -202,7 +202,7 @@ export class NotificationsService implements NotificationsServiceAbstraction {
|
||||
}
|
||||
|
||||
private async isAuthedAndUnlocked() {
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
if (await this.stateService.getIsAuthenticated()) {
|
||||
const locked = await this.vaultTimeoutService.isLocked();
|
||||
return !locked;
|
||||
}
|
||||
|
||||
49
common/src/services/organization.service.ts
Normal file
49
common/src/services/organization.service.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { OrganizationService as OrganizationServiceAbstraction } from '../abstractions/organization.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
import { OrganizationData } from '../models/data/organizationData';
|
||||
|
||||
import { Organization } from '../models/domain/organization';
|
||||
|
||||
export class OrganizationService implements OrganizationServiceAbstraction {
|
||||
constructor(private stateService: StateService) {
|
||||
}
|
||||
|
||||
async get(id: string): Promise<Organization> {
|
||||
const organizations = await this.stateService.getOrganizations();
|
||||
if (organizations == null || !organizations.hasOwnProperty(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Organization(organizations[id]);
|
||||
}
|
||||
|
||||
async getByIdentifier(identifier: string): Promise<Organization> {
|
||||
const organizations = await this.getAll();
|
||||
if (organizations == null || organizations.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return organizations.find(o => o.identifier === identifier);
|
||||
}
|
||||
|
||||
async getAll(userId?: string): Promise<Organization[]> {
|
||||
const organizations = await this.stateService.getOrganizations({ userId: userId });
|
||||
const response: Organization[] = [];
|
||||
for (const id in organizations) {
|
||||
if (organizations.hasOwnProperty(id) && !organizations[id].isProviderUser) {
|
||||
response.push(new Organization(organizations[id]));
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async save(organizations: {[id: string]: OrganizationData}) {
|
||||
return await this.stateService.setOrganizations(organizations);
|
||||
}
|
||||
|
||||
async canManageSponsorships(): Promise<boolean> {
|
||||
const orgs = await this.getAll();
|
||||
return orgs.some(o => o.familySponsorshipAvailable || o.familySponsorshipFriendlyName !== null);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
PasswordGenerationService as PasswordGenerationServiceAbstraction,
|
||||
} from '../abstractions/passwordGeneration.service';
|
||||
import { PolicyService } from '../abstractions/policy.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
import { EEFLongWordList } from '../misc/wordlist';
|
||||
|
||||
@@ -34,19 +34,11 @@ const DefaultOptions = {
|
||||
includeNumber: false,
|
||||
};
|
||||
|
||||
const Keys = {
|
||||
options: 'passwordGenerationOptions',
|
||||
history: 'generatedPasswordHistory',
|
||||
};
|
||||
|
||||
const MaxPasswordsInHistory = 100;
|
||||
|
||||
export class PasswordGenerationService implements PasswordGenerationServiceAbstraction {
|
||||
private optionsCache: any;
|
||||
private history: GeneratedPasswordHistory[];
|
||||
|
||||
constructor(private cryptoService: CryptoService, private storageService: StorageService,
|
||||
private policyService: PolicyService) { }
|
||||
constructor(private cryptoService: CryptoService, private policyService: PolicyService,
|
||||
private stateService: StateService) { }
|
||||
|
||||
async generatePassword(options: any): Promise<string> {
|
||||
// overload defaults with given options
|
||||
@@ -188,17 +180,16 @@ export class PasswordGenerationService implements PasswordGenerationServiceAbstr
|
||||
}
|
||||
|
||||
async getOptions(): Promise<[any, PasswordGeneratorPolicyOptions]> {
|
||||
if (this.optionsCache == null) {
|
||||
const options = await this.storageService.get(Keys.options);
|
||||
if (options == null) {
|
||||
this.optionsCache = DefaultOptions;
|
||||
} else {
|
||||
this.optionsCache = Object.assign({}, DefaultOptions, options);
|
||||
}
|
||||
let options = await this.stateService.getPasswordGenerationOptions();
|
||||
if (options == null) {
|
||||
options = DefaultOptions;
|
||||
} else {
|
||||
options = Object.assign({}, DefaultOptions, options);
|
||||
}
|
||||
const enforcedOptions = await this.enforcePasswordGeneratorPoliciesOnOptions(this.optionsCache);
|
||||
this.optionsCache = enforcedOptions[0];
|
||||
return [this.optionsCache, enforcedOptions[1]];
|
||||
await this.stateService.setPasswordGenerationOptions(options);
|
||||
const enforcedOptions = await this.enforcePasswordGeneratorPoliciesOnOptions(options);
|
||||
options = enforcedOptions[0];
|
||||
return [options, enforcedOptions[1]];
|
||||
}
|
||||
|
||||
async enforcePasswordGeneratorPoliciesOnOptions(options: any): Promise<[any, PasswordGeneratorPolicyOptions]> {
|
||||
@@ -332,8 +323,7 @@ export class PasswordGenerationService implements PasswordGenerationServiceAbstr
|
||||
}
|
||||
|
||||
async saveOptions(options: any) {
|
||||
await this.storageService.save(Keys.options, options);
|
||||
this.optionsCache = options;
|
||||
await this.stateService.setPasswordGenerationOptions(options);
|
||||
}
|
||||
|
||||
async getHistory(): Promise<GeneratedPasswordHistory[]> {
|
||||
@@ -342,12 +332,16 @@ export class PasswordGenerationService implements PasswordGenerationServiceAbstr
|
||||
return new Array<GeneratedPasswordHistory>();
|
||||
}
|
||||
|
||||
if (!this.history) {
|
||||
const encrypted = await this.storageService.get<GeneratedPasswordHistory[]>(Keys.history);
|
||||
this.history = await this.decryptHistory(encrypted);
|
||||
if (await this.stateService.getDecryptedPasswordGenerationHistory() != null) {
|
||||
const encrypted = await this.stateService.getEncryptedPasswordGenerationHistory();
|
||||
const decrypted = await this.decryptHistory(encrypted);
|
||||
await this.stateService.setDecryptedPasswordGenerationHistory(decrypted);
|
||||
}
|
||||
|
||||
return this.history || new Array<GeneratedPasswordHistory>();
|
||||
const passwordGenerationHistory = await this.stateService.getDecryptedPasswordGenerationHistory();
|
||||
return passwordGenerationHistory != null ?
|
||||
passwordGenerationHistory :
|
||||
new Array<GeneratedPasswordHistory>();
|
||||
}
|
||||
|
||||
async addHistory(password: string): Promise<any> {
|
||||
@@ -372,12 +366,12 @@ export class PasswordGenerationService implements PasswordGenerationServiceAbstr
|
||||
}
|
||||
|
||||
const newHistory = await this.encryptHistory(currentHistory);
|
||||
return await this.storageService.save(Keys.history, newHistory);
|
||||
return await this.stateService.setEncryptedPasswordGenerationHistory(newHistory);
|
||||
}
|
||||
|
||||
async clear(): Promise<any> {
|
||||
this.history = [];
|
||||
return await this.storageService.remove(Keys.history);
|
||||
async clear(userId?: string): Promise<any> {
|
||||
await this.stateService.setEncryptedPasswordGenerationHistory(null, { userId: userId });
|
||||
await this.stateService.setDecryptedPasswordGenerationHistory(null, { userId: userId });
|
||||
}
|
||||
|
||||
passwordStrength(password: string, userInputs: string[] = null): zxcvbn.ZXCVBNResult {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { OrganizationService } from '../abstractions/organization.service';
|
||||
import { PolicyService as PolicyServiceAbstraction } from '../abstractions/policy.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
import { PolicyData } from '../models/data/policyData';
|
||||
|
||||
@@ -17,43 +17,40 @@ import { ApiService } from '../abstractions/api.service';
|
||||
import { ListResponse } from '../models/response/listResponse';
|
||||
import { PolicyResponse } from '../models/response/policyResponse';
|
||||
|
||||
const Keys = {
|
||||
policiesPrefix: 'policies_',
|
||||
};
|
||||
|
||||
export class PolicyService implements PolicyServiceAbstraction {
|
||||
policyCache: Policy[];
|
||||
|
||||
constructor(private userService: UserService, private storageService: StorageService,
|
||||
constructor(private stateService: StateService, private organizationService: OrganizationService,
|
||||
private apiService: ApiService) {
|
||||
}
|
||||
|
||||
clearCache(): void {
|
||||
this.policyCache = null;
|
||||
async clearCache(): Promise<void> {
|
||||
await this.stateService.setDecryptedPolicies(null);
|
||||
}
|
||||
|
||||
async getAll(type?: PolicyType): Promise<Policy[]> {
|
||||
if (this.policyCache == null) {
|
||||
const userId = await this.userService.getUserId();
|
||||
const policies = await this.storageService.get<{ [id: string]: PolicyData; }>(
|
||||
Keys.policiesPrefix + userId);
|
||||
const response: Policy[] = [];
|
||||
for (const id in policies) {
|
||||
if (policies.hasOwnProperty(id)) {
|
||||
response.push(new Policy(policies[id]));
|
||||
async getAll(type?: PolicyType, userId?: string): Promise<Policy[]> {
|
||||
let response: Policy[] = [];
|
||||
const decryptedPolicies = await this.stateService.getDecryptedPolicies({ userId: userId });
|
||||
if (decryptedPolicies != null) {
|
||||
response = decryptedPolicies;
|
||||
} else {
|
||||
const diskPolicies = await this.stateService.getEncryptedPolicies({ userId: userId });
|
||||
for (const id in diskPolicies) {
|
||||
if (diskPolicies.hasOwnProperty(id)) {
|
||||
response.push(new Policy(diskPolicies[id]));
|
||||
}
|
||||
}
|
||||
this.policyCache = response;
|
||||
await this.stateService.setDecryptedPolicies(response, { userId: userId });
|
||||
}
|
||||
if (type != null) {
|
||||
return this.policyCache.filter(p => p.type === type);
|
||||
return response.filter(policy => policy.type === type);
|
||||
} else {
|
||||
return this.policyCache;
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
async getPolicyForOrganization(policyType: PolicyType, organizationId: string): Promise<Policy> {
|
||||
const org = await this.userService.getOrganization(organizationId);
|
||||
const org = await this.organizationService.get(organizationId);
|
||||
if (org?.isProviderUser) {
|
||||
const orgPolicies = await this.apiService.getPolicies(organizationId);
|
||||
const policy = orgPolicies.data.find(p => p.organizationId === organizationId);
|
||||
@@ -70,14 +67,13 @@ export class PolicyService implements PolicyServiceAbstraction {
|
||||
}
|
||||
|
||||
async replace(policies: { [id: string]: PolicyData; }): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
await this.storageService.save(Keys.policiesPrefix + userId, policies);
|
||||
this.policyCache = null;
|
||||
await this.stateService.setDecryptedPolicies(null);
|
||||
await this.stateService.setEncryptedPolicies(policies);
|
||||
}
|
||||
|
||||
async clear(userId: string): Promise<any> {
|
||||
await this.storageService.remove(Keys.policiesPrefix + userId);
|
||||
this.policyCache = null;
|
||||
async clear(userId?: string): Promise<any> {
|
||||
await this.stateService.setDecryptedPolicies(null, { userId: userId });
|
||||
await this.stateService.setEncryptedPolicies(null, { userId: userId });
|
||||
}
|
||||
|
||||
async getMasterPasswordPolicyOptions(policies?: Policy[]): Promise<MasterPasswordPolicyOptions> {
|
||||
@@ -187,9 +183,9 @@ export class PolicyService implements PolicyServiceAbstraction {
|
||||
return policiesData.map(p => new Policy(p));
|
||||
}
|
||||
|
||||
async policyAppliesToUser(policyType: PolicyType, policyFilter?: (policy: Policy) => boolean) {
|
||||
const policies = await this.getAll(policyType);
|
||||
const organizations = await this.userService.getAllOrganizations();
|
||||
async policyAppliesToUser(policyType: PolicyType, policyFilter?: (policy: Policy) => boolean, userId?: string) {
|
||||
const policies = await this.getAll(policyType, userId);
|
||||
const organizations = await this.organizationService.getAll(userId);
|
||||
let filteredPolicies;
|
||||
|
||||
if (policyFilter != null) {
|
||||
|
||||
35
common/src/services/provider.service.ts
Normal file
35
common/src/services/provider.service.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { ProviderService as ProviderServiceAbstraction } from '../abstractions/provider.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
import { ProviderData } from '../models/data/providerData';
|
||||
|
||||
import { Provider } from '../models/domain/provider';
|
||||
|
||||
export class ProviderService implements ProviderServiceAbstraction {
|
||||
constructor(private stateService: StateService) {
|
||||
}
|
||||
|
||||
async get(id: string): Promise<Provider> {
|
||||
const providers = await this.stateService.getProviders();
|
||||
if (providers == null || !providers.hasOwnProperty(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Provider(providers[id]);
|
||||
}
|
||||
|
||||
async getAll(): Promise<Provider[]> {
|
||||
const providers = await this.stateService.getProviders();
|
||||
const response: Provider[] = [];
|
||||
for (const id in providers) {
|
||||
if (providers.hasOwnProperty(id)) {
|
||||
response.push(new Provider(providers[id]));
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async save(providers: { [id: string]: ProviderData; }) {
|
||||
await this.stateService.setProviders(providers);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import { SendFile } from '../models/domain/sendFile';
|
||||
import { SendText } from '../models/domain/sendText';
|
||||
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
|
||||
import { FileUploadType } from '../enums/fileUploadType';
|
||||
import { SendType } from '../enums/sendType';
|
||||
|
||||
import { SendView } from '../models/view/sendView';
|
||||
@@ -23,25 +22,17 @@ import { CryptoFunctionService } from '../abstractions/cryptoFunction.service';
|
||||
import { FileUploadService } from '../abstractions/fileUpload.service';
|
||||
import { I18nService } from '../abstractions/i18n.service';
|
||||
import { SendService as SendServiceAbstraction } from '../abstractions/send.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
import { Utils } from '../misc/utils';
|
||||
|
||||
const Keys = {
|
||||
sendsPrefix: 'sends_',
|
||||
};
|
||||
|
||||
export class SendService implements SendServiceAbstraction {
|
||||
decryptedSendCache: SendView[];
|
||||
constructor(private cryptoService: CryptoService, private apiService: ApiService,
|
||||
private fileUploadService: FileUploadService, private i18nService: I18nService,
|
||||
private cryptoFunctionService: CryptoFunctionService, private stateService: StateService) { }
|
||||
|
||||
constructor(private cryptoService: CryptoService, private userService: UserService,
|
||||
private apiService: ApiService, private fileUploadService: FileUploadService,
|
||||
private storageService: StorageService, private i18nService: I18nService,
|
||||
private cryptoFunctionService: CryptoFunctionService) { }
|
||||
|
||||
clearCache(): void {
|
||||
this.decryptedSendCache = null;
|
||||
async clearCache(): Promise<void> {
|
||||
await this.stateService.setDecryptedSends(null);
|
||||
}
|
||||
|
||||
async encrypt(model: SendView, file: File | ArrayBuffer, password: string,
|
||||
@@ -85,9 +76,7 @@ export class SendService implements SendServiceAbstraction {
|
||||
}
|
||||
|
||||
async get(id: string): Promise<Send> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const sends = await this.storageService.get<{ [id: string]: SendData; }>(
|
||||
Keys.sendsPrefix + userId);
|
||||
const sends = await this.stateService.getEncryptedSends();
|
||||
if (sends == null || !sends.hasOwnProperty(id)) {
|
||||
return null;
|
||||
}
|
||||
@@ -96,9 +85,7 @@ export class SendService implements SendServiceAbstraction {
|
||||
}
|
||||
|
||||
async getAll(): Promise<Send[]> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const sends = await this.storageService.get<{ [id: string]: SendData; }>(
|
||||
Keys.sendsPrefix + userId);
|
||||
const sends = await this.stateService.getEncryptedSends();
|
||||
const response: Send[] = [];
|
||||
for (const id in sends) {
|
||||
if (sends.hasOwnProperty(id)) {
|
||||
@@ -109,16 +96,17 @@ export class SendService implements SendServiceAbstraction {
|
||||
}
|
||||
|
||||
async getAllDecrypted(): Promise<SendView[]> {
|
||||
if (this.decryptedSendCache != null) {
|
||||
return this.decryptedSendCache;
|
||||
let decSends = await this.stateService.getDecryptedSends();
|
||||
if (decSends != null) {
|
||||
return decSends;
|
||||
}
|
||||
|
||||
decSends = [];
|
||||
const hasKey = await this.cryptoService.hasKey();
|
||||
if (!hasKey) {
|
||||
throw new Error('No key.');
|
||||
}
|
||||
|
||||
const decSends: SendView[] = [];
|
||||
const promises: Promise<any>[] = [];
|
||||
const sends = await this.getAll();
|
||||
sends.forEach(send => {
|
||||
@@ -128,8 +116,8 @@ export class SendService implements SendServiceAbstraction {
|
||||
await Promise.all(promises);
|
||||
decSends.sort(Utils.getSortFunction(this.i18nService, 'name'));
|
||||
|
||||
this.decryptedSendCache = decSends;
|
||||
return this.decryptedSendCache;
|
||||
await this.stateService.setDecryptedSends(decSends);
|
||||
return decSends;
|
||||
}
|
||||
|
||||
async saveWithServer(sendData: [Send, EncArrayBuffer]): Promise<any> {
|
||||
@@ -160,7 +148,7 @@ export class SendService implements SendServiceAbstraction {
|
||||
response = await this.apiService.putSend(sendData[0].id, request);
|
||||
}
|
||||
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
const data = new SendData(response, userId);
|
||||
await this.upsert(data);
|
||||
}
|
||||
@@ -191,9 +179,7 @@ export class SendService implements SendServiceAbstraction {
|
||||
}
|
||||
|
||||
async upsert(send: SendData | SendData[]): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
let sends = await this.storageService.get<{ [id: string]: SendData; }>(
|
||||
Keys.sendsPrefix + userId);
|
||||
let sends = await this.stateService.getEncryptedSends();
|
||||
if (sends == null) {
|
||||
sends = {};
|
||||
}
|
||||
@@ -207,25 +193,21 @@ export class SendService implements SendServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.sendsPrefix + userId, sends);
|
||||
this.decryptedSendCache = null;
|
||||
await this.replace(sends);
|
||||
}
|
||||
|
||||
async replace(sends: { [id: string]: SendData; }): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
await this.storageService.save(Keys.sendsPrefix + userId, sends);
|
||||
this.decryptedSendCache = null;
|
||||
await this.stateService.setDecryptedSends(null);
|
||||
await this.stateService.setEncryptedSends(sends);
|
||||
}
|
||||
|
||||
async clear(userId: string): Promise<any> {
|
||||
await this.storageService.remove(Keys.sendsPrefix + userId);
|
||||
this.decryptedSendCache = null;
|
||||
async clear(): Promise<any> {
|
||||
await this.stateService.setDecryptedSends(null);
|
||||
await this.stateService.setEncryptedSends(null);
|
||||
}
|
||||
|
||||
async delete(id: string | string[]): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
const sends = await this.storageService.get<{ [id: string]: SendData; }>(
|
||||
Keys.sendsPrefix + userId);
|
||||
const sends = await this.stateService.getEncryptedSends();
|
||||
if (sends == null) {
|
||||
return;
|
||||
}
|
||||
@@ -241,8 +223,7 @@ export class SendService implements SendServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.sendsPrefix + userId, sends);
|
||||
this.decryptedSendCache = null;
|
||||
await this.replace(sends);
|
||||
}
|
||||
|
||||
async deleteWithServer(id: string): Promise<any> {
|
||||
@@ -252,7 +233,7 @@ export class SendService implements SendServiceAbstraction {
|
||||
|
||||
async removePasswordWithServer(id: string): Promise<any> {
|
||||
const response = await this.apiService.putSendRemovePassword(id);
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
const data = new SendData(response, userId);
|
||||
await this.upsert(data);
|
||||
}
|
||||
@@ -270,7 +251,7 @@ export class SendService implements SendServiceAbstraction {
|
||||
reject(e);
|
||||
}
|
||||
};
|
||||
reader.onerror = evt => {
|
||||
reader.onerror = () => {
|
||||
reject('Error reading file.');
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { SettingsService as SettingsServiceAbstraction } from '../abstractions/settings.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
|
||||
const Keys = {
|
||||
settingsPrefix: 'settings_',
|
||||
@@ -8,13 +7,11 @@ const Keys = {
|
||||
};
|
||||
|
||||
export class SettingsService implements SettingsServiceAbstraction {
|
||||
private settingsCache: any;
|
||||
|
||||
constructor(private userService: UserService, private storageService: StorageService) {
|
||||
constructor(private stateService: StateService) {
|
||||
}
|
||||
|
||||
clearCache(): void {
|
||||
this.settingsCache = null;
|
||||
async clearCache(): Promise<void> {
|
||||
await this.stateService.setSettings(null);
|
||||
}
|
||||
|
||||
getEquivalentDomains(): Promise<any> {
|
||||
@@ -25,19 +22,18 @@ export class SettingsService implements SettingsServiceAbstraction {
|
||||
await this.setSettingsKey(Keys.equivalentDomains, equivalentDomains);
|
||||
}
|
||||
|
||||
async clear(userId: string): Promise<void> {
|
||||
await this.storageService.remove(Keys.settingsPrefix + userId);
|
||||
this.clearCache();
|
||||
async clear(userId?: string): Promise<void> {
|
||||
await this.stateService.setSettings(null, { userId: userId });
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
private async getSettings(): Promise<any> {
|
||||
if (this.settingsCache == null) {
|
||||
const userId = await this.userService.getUserId();
|
||||
this.settingsCache = this.storageService.get(Keys.settingsPrefix + userId);
|
||||
const settings = await this.stateService.getSettings();
|
||||
if (settings == null) {
|
||||
const userId = await this.stateService.getUserId();
|
||||
}
|
||||
return this.settingsCache;
|
||||
return settings;
|
||||
}
|
||||
|
||||
private async getSettingsKey(key: string): Promise<any> {
|
||||
@@ -49,14 +45,12 @@ export class SettingsService implements SettingsServiceAbstraction {
|
||||
}
|
||||
|
||||
private async setSettingsKey(key: string, value: any): Promise<void> {
|
||||
const userId = await this.userService.getUserId();
|
||||
let settings = await this.getSettings();
|
||||
if (!settings) {
|
||||
settings = {};
|
||||
}
|
||||
|
||||
settings[key] = value;
|
||||
await this.storageService.save(Keys.settingsPrefix + userId, settings);
|
||||
this.settingsCache = settings;
|
||||
await this.stateService.setSettings(settings);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
335
common/src/services/stateMigration.service.ts
Normal file
335
common/src/services/stateMigration.service.ts
Normal file
@@ -0,0 +1,335 @@
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
|
||||
import { Account } from '../models/domain/account';
|
||||
import { GeneratedPasswordHistory } from '../models/domain/generatedPasswordHistory';
|
||||
import { State } from '../models/domain/state';
|
||||
import { StorageOptions } from '../models/domain/storageOptions';
|
||||
|
||||
import { CipherData } from '../models/data/cipherData';
|
||||
import { CollectionData } from '../models/data/collectionData';
|
||||
import { EventData } from '../models/data/eventData';
|
||||
import { FolderData } from '../models/data/folderData';
|
||||
import { OrganizationData } from '../models/data/organizationData';
|
||||
import { PolicyData } from '../models/data/policyData';
|
||||
import { ProviderData } from '../models/data/providerData';
|
||||
import { SendData } from '../models/data/sendData';
|
||||
|
||||
import { HtmlStorageLocation } from '../enums/htmlStorageLocation';
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
|
||||
// Originally (before January 2022) storage was handled as a flat key/value pair store.
|
||||
// With the move to a typed object for state storage these keys should no longer be in use anywhere outside of this migration.
|
||||
const v1Keys = {
|
||||
accessToken: 'accessToken',
|
||||
alwaysShowDock: 'alwaysShowDock',
|
||||
autoConfirmFingerprints: 'autoConfirmFingerprints',
|
||||
autoFillOnPageLoadDefault: 'autoFillOnPageLoadDefault',
|
||||
biometricAwaitingAcceptance: 'biometricAwaitingAcceptance',
|
||||
biometricFingerprintValidated: 'biometricFingerprintValidated',
|
||||
biometricText: 'biometricText',
|
||||
biometricUnlock: 'biometric',
|
||||
clearClipboard: 'clearClipboardKey',
|
||||
clientId: 'clientId',
|
||||
clientSecret: 'clientSecret',
|
||||
collapsedGroupings: 'collapsedGroupings',
|
||||
convertAccountToKeyConnector: 'convertAccountToKeyConnector',
|
||||
defaultUriMatch: 'defaultUriMatch',
|
||||
disableAddLoginNotification: 'disableAddLoginNotification',
|
||||
disableAutoBiometricsPrompt: 'noAutoPromptBiometrics',
|
||||
disableAutoTotpCopy: 'disableAutoTotpCopy',
|
||||
disableBadgeCounter: 'disableBadgeCounter',
|
||||
disableChangedPasswordNotification: 'disableChangedPasswordNotification',
|
||||
disableContextMenuItem: 'disableContextMenuItem',
|
||||
disableFavicon: 'disableFavicon',
|
||||
disableGa: 'disableGa',
|
||||
dontShowCardsCurrentTab: 'dontShowCardsCurrentTab',
|
||||
dontShowIdentitiesCurrentTab: 'dontShowIdentitiesCurrentTab',
|
||||
emailVerified: 'emailVerified',
|
||||
enableAlwaysOnTop: 'enableAlwaysOnTopKey',
|
||||
enableAutoFillOnPageLoad: 'enableAutoFillOnPageLoad',
|
||||
enableBiometric: 'enabledBiometric',
|
||||
enableBrowserIntegration: 'enableBrowserIntegration',
|
||||
enableBrowserIntegrationFingerprint: 'enableBrowserIntegrationFingerprint',
|
||||
enableCloseToTray: 'enableCloseToTray',
|
||||
enableFullWidth: 'enableFullWidth',
|
||||
enableGravatars: 'enableGravatars',
|
||||
enableMinimizeToTray: 'enableMinimizeToTray',
|
||||
enableStartToTray: 'enableStartToTrayKey',
|
||||
enableTray: 'enableTray',
|
||||
encKey: 'encKey', // Generated Symmetric Key
|
||||
encOrgKeys: 'encOrgKeys',
|
||||
encPrivate: 'encPrivateKey',
|
||||
encProviderKeys: 'encProviderKeys',
|
||||
entityId: 'entityId',
|
||||
entityType: 'entityType',
|
||||
environmentUrls: 'environmentUrls',
|
||||
equivalentDomains: 'equivalentDomains',
|
||||
eventCollection: 'eventCollection',
|
||||
forcePasswordReset: 'forcePasswordReset',
|
||||
history: 'generatedPasswordHistory',
|
||||
installedVersion: 'installedVersion',
|
||||
kdf: 'kdf',
|
||||
kdfIterations: 'kdfIterations',
|
||||
key: 'key', // Master Key
|
||||
keyHash: 'keyHash',
|
||||
lastActive: 'lastActive',
|
||||
localData: 'sitesLocalData',
|
||||
locale: 'locale',
|
||||
mainWindowSize: 'mainWindowSize',
|
||||
minimizeOnCopyToClipboard: 'minimizeOnCopyToClipboardKey',
|
||||
neverDomains: 'neverDomains',
|
||||
noAutoPromptBiometricsText: 'noAutoPromptBiometricsText',
|
||||
openAtLogin: 'openAtLogin',
|
||||
passwordGenerationOptions: 'passwordGenerationOptions',
|
||||
pinProtected: 'pinProtectedKey',
|
||||
protectedPin: 'protectedPin',
|
||||
refreshToken: 'refreshToken',
|
||||
ssoCodeVerifier: 'ssoCodeVerifier',
|
||||
ssoIdentifier: 'ssoOrgIdentifier',
|
||||
ssoState: 'ssoState',
|
||||
stamp: 'securityStamp',
|
||||
theme: 'theme',
|
||||
userEmail: 'userEmail',
|
||||
userId: 'userId',
|
||||
usesConnector: 'usesKeyConnector',
|
||||
vaultTimeoutAction: 'vaultTimeoutAction',
|
||||
vaultTimeout: 'lockOption',
|
||||
rememberedEmail: 'rememberedEmail',
|
||||
};
|
||||
|
||||
const v1KeyPrefixes = {
|
||||
ciphers: 'ciphers_',
|
||||
collections: 'collections_',
|
||||
folders: 'folders_',
|
||||
lastSync: 'lastSync_',
|
||||
policies: 'policies_',
|
||||
twoFactorToken: 'twoFactorToken_',
|
||||
organizations: 'organizations_',
|
||||
providers: 'providers_',
|
||||
sends: 'sends_',
|
||||
settings: 'settings_',
|
||||
};
|
||||
|
||||
|
||||
export class StateMigrationService {
|
||||
readonly latestVersion: number = 2;
|
||||
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
private secureStorageService: StorageService
|
||||
) {}
|
||||
|
||||
async needsMigration(): Promise<boolean> {
|
||||
const currentStateVersion = (await this.storageService.get<State>('state'))?.globals?.stateVersion;
|
||||
return currentStateVersion == null || currentStateVersion < this.latestVersion;
|
||||
}
|
||||
|
||||
async migrate(): Promise<void> {
|
||||
let currentStateVersion = (await this.storageService.get<State>('state'))?.globals?.stateVersion ?? 1;
|
||||
while (currentStateVersion < this.latestVersion) {
|
||||
switch (currentStateVersion) {
|
||||
case 1:
|
||||
await this.migrateStateFrom1To2();
|
||||
break;
|
||||
}
|
||||
|
||||
currentStateVersion += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private async migrateStateFrom1To2(): Promise<void> {
|
||||
const options: StorageOptions = { htmlStorageLocation: HtmlStorageLocation.Local };
|
||||
const userId = await this.storageService.get<string>('userId');
|
||||
const initialState: State = userId == null ?
|
||||
{
|
||||
globals: {
|
||||
stateVersion: 2,
|
||||
},
|
||||
accounts: {},
|
||||
activeUserId: null,
|
||||
} :
|
||||
{
|
||||
activeUserId: userId,
|
||||
globals: {
|
||||
biometricAwaitingAcceptance: await this.storageService.get<boolean>(v1Keys.biometricAwaitingAcceptance, options),
|
||||
biometricFingerprintValidated: await this.storageService.get<boolean>(v1Keys.biometricFingerprintValidated, options),
|
||||
biometricText: await this.storageService.get<string>(v1Keys.biometricText, options),
|
||||
disableFavicon: await this.storageService.get<boolean>(v1Keys.disableFavicon, options),
|
||||
enableAlwaysOnTop: await this.storageService.get<boolean>(v1Keys.enableAlwaysOnTop, options),
|
||||
enableBiometrics: await this.storageService.get<boolean>(v1Keys.enableBiometric, options),
|
||||
installedVersion: await this.storageService.get<string>(v1Keys.installedVersion, options),
|
||||
lastActive: await this.storageService.get<number>(v1Keys.lastActive, options),
|
||||
locale: await this.storageService.get<string>(v1Keys.locale, options),
|
||||
loginRedirect: null,
|
||||
mainWindowSize: null,
|
||||
noAutoPromptBiometrics: await this.storageService.get<boolean>(v1Keys.disableAutoBiometricsPrompt, options),
|
||||
noAutoPromptBiometricsText: await this.storageService.get<string>(v1Keys.noAutoPromptBiometricsText, options),
|
||||
openAtLogin: await this.storageService.get<boolean>(v1Keys.openAtLogin, options),
|
||||
organizationInvitation: await this.storageService.get<string>('', options),
|
||||
rememberedEmail: await this.storageService.get<string>(v1Keys.rememberedEmail, options),
|
||||
stateVersion: 2,
|
||||
theme: await this.storageService.get<string>(v1Keys.theme, options),
|
||||
twoFactorToken: await this.storageService.get<string>(v1KeyPrefixes.twoFactorToken + userId, options),
|
||||
vaultTimeout: await this.storageService.get<number>(v1Keys.vaultTimeout, options),
|
||||
vaultTimeoutAction: await this.storageService.get<string>(v1Keys.vaultTimeoutAction, options),
|
||||
window: null,
|
||||
},
|
||||
accounts: {
|
||||
[userId]: new Account({
|
||||
data: {
|
||||
addEditCipherInfo: null,
|
||||
ciphers: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: CipherData }>(v1KeyPrefixes.ciphers + userId, options),
|
||||
},
|
||||
collapsedGroupings: null,
|
||||
collections: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: CollectionData }>(v1KeyPrefixes.collections + userId, options),
|
||||
},
|
||||
eventCollection: await this.storageService.get<EventData[]>(v1Keys.eventCollection, options),
|
||||
folders: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: FolderData }>(v1KeyPrefixes.folders + userId, options),
|
||||
},
|
||||
localData: null,
|
||||
organizations: await this.storageService.get<{ [id: string]: OrganizationData }>(v1KeyPrefixes.organizations + userId),
|
||||
passwordGenerationHistory: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<GeneratedPasswordHistory[]>('TODO', options), // TODO: Whats up here?
|
||||
},
|
||||
policies: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: PolicyData }>(v1KeyPrefixes.policies + userId, options),
|
||||
},
|
||||
providers: await this.storageService.get<{ [id: string]: ProviderData }>(v1KeyPrefixes.providers + userId),
|
||||
sends: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: SendData }>(v1KeyPrefixes.sends, options),
|
||||
},
|
||||
},
|
||||
keys: {
|
||||
apiKeyClientSecret: await this.storageService.get<string>(v1Keys.clientSecret, options),
|
||||
cryptoMasterKey: null,
|
||||
cryptoMasterKeyAuto: null,
|
||||
cryptoMasterKeyB64: null,
|
||||
cryptoMasterKeyBiometric: null,
|
||||
cryptoSymmetricKey: {
|
||||
encrypted: await this.storageService.get<string>(v1Keys.encKey, options),
|
||||
decrypted: null,
|
||||
},
|
||||
legacyEtmKey: null,
|
||||
organizationKeys: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<any>(v1Keys.encOrgKeys + userId, options),
|
||||
},
|
||||
privateKey: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<string>(v1Keys.encPrivate, options),
|
||||
},
|
||||
providerKeys: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<any>(v1Keys.encProviderKeys + userId, options),
|
||||
},
|
||||
publicKey: null,
|
||||
},
|
||||
profile: {
|
||||
apiKeyClientId: await this.storageService.get<string>(v1Keys.clientId, options),
|
||||
authenticationStatus: null,
|
||||
convertAccountToKeyConnector: await this.storageService.get<boolean>(v1Keys.convertAccountToKeyConnector, options),
|
||||
email: await this.storageService.get<string>(v1Keys.userEmail, options),
|
||||
emailVerified: await this.storageService.get<boolean>(v1Keys.emailVerified, options),
|
||||
entityId: null,
|
||||
entityType: null,
|
||||
everBeenUnlocked: null,
|
||||
forcePasswordReset: null,
|
||||
hasPremiumPersonally: null,
|
||||
kdfIterations: await this.storageService.get<number>(v1Keys.kdfIterations, options),
|
||||
kdfType: await this.storageService.get<KdfType>(v1Keys.kdf, options),
|
||||
keyHash: await this.storageService.get<string>(v1Keys.keyHash, options),
|
||||
lastActive: await this.storageService.get<number>(v1Keys.lastActive, options),
|
||||
lastSync: null,
|
||||
ssoCodeVerifier: await this.storageService.get<string>(v1Keys.ssoCodeVerifier, options),
|
||||
ssoOrganizationIdentifier: await this.storageService.get<string>(v1Keys.ssoIdentifier, options),
|
||||
ssoState: null,
|
||||
userId: userId,
|
||||
usesKeyConnector: null,
|
||||
},
|
||||
settings: {
|
||||
alwaysShowDock: await this.storageService.get<boolean>(v1Keys.alwaysShowDock, options),
|
||||
autoConfirmFingerPrints: await this.storageService.get<boolean>(v1Keys.autoConfirmFingerprints, options),
|
||||
autoFillOnPageLoadDefault: await this.storageService.get<boolean>(v1Keys.autoFillOnPageLoadDefault, options),
|
||||
biometricLocked: null,
|
||||
biometricUnlock: await this.storageService.get<boolean>(v1Keys.biometricUnlock, options),
|
||||
clearClipboard: await this.storageService.get<number>(v1Keys.clearClipboard, options),
|
||||
defaultUriMatch: await this.storageService.get<any>(v1Keys.defaultUriMatch, options),
|
||||
disableAddLoginNotification: await this.storageService.get<boolean>(v1Keys.disableAddLoginNotification, options),
|
||||
disableAutoBiometricsPrompt: await this.storageService.get<boolean>(v1Keys.disableAutoBiometricsPrompt, options),
|
||||
disableAutoTotpCopy: await this.storageService.get<boolean>(v1Keys.disableAutoTotpCopy, options),
|
||||
disableBadgeCounter: await this.storageService.get<boolean>(v1Keys.disableBadgeCounter, options),
|
||||
disableChangedPasswordNotification: await this.storageService.get<boolean>(v1Keys.disableChangedPasswordNotification, options),
|
||||
disableContextMenuItem: await this.storageService.get<boolean>(v1Keys.disableContextMenuItem, options),
|
||||
disableGa: await this.storageService.get<boolean>(v1Keys.disableGa, options),
|
||||
dontShowCardsCurrentTab: await this.storageService.get<boolean>(v1Keys.dontShowCardsCurrentTab, options),
|
||||
dontShowIdentitiesCurrentTab: await this.storageService.get<boolean>(v1Keys.dontShowIdentitiesCurrentTab, options),
|
||||
enableAlwaysOnTop: await this.storageService.get<boolean>(v1Keys.enableAlwaysOnTop, options),
|
||||
enableAutoFillOnPageLoad: await this.storageService.get<boolean>(v1Keys.enableAutoFillOnPageLoad, options),
|
||||
enableBiometric: await this.storageService.get<boolean>(v1Keys.enableBiometric, options),
|
||||
enableBrowserIntegration: await this.storageService.get<boolean>(v1Keys.enableBrowserIntegration, options),
|
||||
enableBrowserIntegrationFingerprint: await this.storageService.get<boolean>(v1Keys.enableBrowserIntegrationFingerprint, options),
|
||||
enableCloseToTray: await this.storageService.get<boolean>(v1Keys.enableCloseToTray, options),
|
||||
enableFullWidth: await this.storageService.get<boolean>(v1Keys.enableFullWidth, options),
|
||||
enableGravitars: await this.storageService.get<boolean>(v1Keys.enableGravatars, options),
|
||||
enableMinimizeToTray: await this.storageService.get<boolean>(v1Keys.enableMinimizeToTray, options),
|
||||
enableStartToTray: await this.storageService.get<boolean>(v1Keys.enableStartToTray, options),
|
||||
enableTray: await this.storageService.get<boolean>(v1Keys.enableTray, options),
|
||||
environmentUrls: await this.storageService.get<any>(v1Keys.environmentUrls, options),
|
||||
equivalentDomains: await this.storageService.get<any>(v1Keys.equivalentDomains, options),
|
||||
minimizeOnCopyToClipboard: await this.storageService.get<boolean>(v1Keys.minimizeOnCopyToClipboard, options),
|
||||
neverDomains: await this.storageService.get<any>(v1Keys.neverDomains, options),
|
||||
openAtLogin: await this.storageService.get<boolean>(v1Keys.openAtLogin, options),
|
||||
passwordGenerationOptions: await this.storageService.get<any>(v1Keys.passwordGenerationOptions, options),
|
||||
pinProtected: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<string>(v1Keys.pinProtected, options),
|
||||
},
|
||||
protectedPin: await this.storageService.get<string>(v1Keys.protectedPin, options),
|
||||
settings: await this.storageService.get<any>(v1KeyPrefixes.settings + userId, options),
|
||||
vaultTimeout: await this.storageService.get<number>(v1Keys.vaultTimeout, options),
|
||||
vaultTimeoutAction: await this.storageService.get<string>(v1Keys.vaultTimeoutAction, options),
|
||||
},
|
||||
tokens: {
|
||||
accessToken: await this.storageService.get<string>(v1Keys.accessToken, options),
|
||||
decodedToken: null,
|
||||
refreshToken: await this.storageService.get<string>(v1Keys.refreshToken, options),
|
||||
securityStamp: null,
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
await this.storageService.save('state', initialState, options);
|
||||
|
||||
if (await this.secureStorageService.has(v1Keys.key, { keySuffix: 'biometric' })) {
|
||||
await this.secureStorageService.save(
|
||||
`${userId}_masterkey_biometric`,
|
||||
await this.secureStorageService.get(v1Keys.key, { keySuffix: 'biometric' }),
|
||||
{ keySuffix: 'biometric' });
|
||||
await this.secureStorageService.remove(v1Keys.key, { keySuffix: 'biometric' });
|
||||
}
|
||||
|
||||
if (await this.secureStorageService.has(v1Keys.key, { keySuffix: 'auto' })) {
|
||||
await this.secureStorageService.save(
|
||||
`${userId}_masterkey_auto`,
|
||||
await this.secureStorageService.get(v1Keys.key, { keySuffix: 'auto' }),
|
||||
{ keySuffix: 'auto' }
|
||||
);
|
||||
await this.secureStorageService.remove(v1Keys.key, { keySuffix: 'auto' });
|
||||
}
|
||||
|
||||
if (await this.secureStorageService.has(v1Keys.key)) {
|
||||
await this.secureStorageService.save(`${userId}_masterkey`, await this.secureStorageService.get(v1Keys.key));
|
||||
await this.secureStorageService.remove(v1Keys.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,13 @@ import { FolderService } from '../abstractions/folder.service';
|
||||
import { KeyConnectorService } from '../abstractions/keyConnector.service';
|
||||
import { LogService } from '../abstractions/log.service';
|
||||
import { MessagingService } from '../abstractions/messaging.service';
|
||||
import { OrganizationService } from '../abstractions/organization.service';
|
||||
import { PolicyService } from '../abstractions/policy.service';
|
||||
import { ProviderService } from '../abstractions/provider.service';
|
||||
import { SendService } from '../abstractions/send.service';
|
||||
import { SettingsService } from '../abstractions/settings.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
import { SyncService as SyncServiceAbstraction } from '../abstractions/sync.service';
|
||||
import { TokenService } from '../abstractions/token.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
|
||||
import { CipherData } from '../models/data/cipherData';
|
||||
import { CollectionData } from '../models/data/collectionData';
|
||||
@@ -35,30 +35,24 @@ import { PolicyResponse } from '../models/response/policyResponse';
|
||||
import { ProfileResponse } from '../models/response/profileResponse';
|
||||
import { SendResponse } from '../models/response/sendResponse';
|
||||
|
||||
const Keys = {
|
||||
lastSyncPrefix: 'lastSync_',
|
||||
};
|
||||
|
||||
export class SyncService implements SyncServiceAbstraction {
|
||||
syncInProgress: boolean = false;
|
||||
|
||||
constructor(private userService: UserService, private apiService: ApiService,
|
||||
private settingsService: SettingsService, private folderService: FolderService,
|
||||
private cipherService: CipherService, private cryptoService: CryptoService,
|
||||
private collectionService: CollectionService, private storageService: StorageService,
|
||||
private messagingService: MessagingService, private policyService: PolicyService,
|
||||
constructor(private apiService: ApiService, private settingsService: SettingsService,
|
||||
private folderService: FolderService, private cipherService: CipherService,
|
||||
private cryptoService: CryptoService, private collectionService: CollectionService,
|
||||
private messagingService: MessagingService, private policyService: PolicyService,
|
||||
private sendService: SendService, private logService: LogService,
|
||||
private tokenService: TokenService, private keyConnectorService: KeyConnectorService,
|
||||
private logoutCallback: (expired: boolean) => Promise<void>) {
|
||||
}
|
||||
private keyConnectorService: KeyConnectorService, private stateService: StateService,
|
||||
private organizationService: OrganizationService, private providerService: ProviderService,
|
||||
private logoutCallback: (expired: boolean) => Promise<void>) { }
|
||||
|
||||
async getLastSync(): Promise<Date> {
|
||||
const userId = await this.userService.getUserId();
|
||||
if (userId == null) {
|
||||
if (await this.stateService.getUserId() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lastSync = await this.storageService.get<any>(Keys.lastSyncPrefix + userId);
|
||||
const lastSync = await this.stateService.getLastSync();
|
||||
if (lastSync) {
|
||||
return new Date(lastSync);
|
||||
}
|
||||
@@ -66,18 +60,13 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
return null;
|
||||
}
|
||||
|
||||
async setLastSync(date: Date): Promise<any> {
|
||||
const userId = await this.userService.getUserId();
|
||||
if (userId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.storageService.save(Keys.lastSyncPrefix + userId, date.toJSON());
|
||||
async setLastSync(date: Date, userId?: string): Promise<any> {
|
||||
await this.stateService.setLastSync(date.toJSON(), { userId: userId });
|
||||
}
|
||||
|
||||
async fullSync(forceSync: boolean, allowThrowOnError = false): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
const isAuthenticated = await this.userService.isAuthenticated();
|
||||
const isAuthenticated = await this.stateService.getIsAuthenticated();
|
||||
if (!isAuthenticated) {
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
@@ -97,7 +86,7 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
try {
|
||||
await this.apiService.refreshIdentityToken();
|
||||
const response = await this.apiService.getSync();
|
||||
@@ -107,7 +96,7 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
await this.syncCollections(response.collections);
|
||||
await this.syncCiphers(userId, response.ciphers);
|
||||
await this.syncSends(userId, response.sends);
|
||||
await this.syncSettings(userId, response.domains);
|
||||
await this.syncSettings(response.domains);
|
||||
await this.syncPolicies(response.policies);
|
||||
|
||||
await this.setLastSync(now);
|
||||
@@ -123,14 +112,14 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
|
||||
async syncUpsertFolder(notification: SyncFolderNotification, isEdit: boolean): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
if (await this.stateService.getIsAuthenticated()) {
|
||||
try {
|
||||
const localFolder = await this.folderService.get(notification.id);
|
||||
if ((!isEdit && localFolder == null) ||
|
||||
(isEdit && localFolder != null && localFolder.revisionDate < notification.revisionDate)) {
|
||||
const remoteFolder = await this.apiService.getFolder(notification.id);
|
||||
if (remoteFolder != null) {
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
await this.folderService.upsert(new FolderData(remoteFolder, userId));
|
||||
this.messagingService.send('syncedUpsertedFolder', { folderId: notification.id });
|
||||
return this.syncCompleted(true);
|
||||
@@ -145,7 +134,7 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
|
||||
async syncDeleteFolder(notification: SyncFolderNotification): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
if (await this.stateService.getIsAuthenticated()) {
|
||||
await this.folderService.delete(notification.id);
|
||||
this.messagingService.send('syncedDeletedFolder', { folderId: notification.id });
|
||||
this.syncCompleted(true);
|
||||
@@ -156,7 +145,7 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
|
||||
async syncUpsertCipher(notification: SyncCipherNotification, isEdit: boolean): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
if (await this.stateService.getIsAuthenticated()) {
|
||||
try {
|
||||
let shouldUpdate = true;
|
||||
const localCipher = await this.cipherService.get(notification.id);
|
||||
@@ -195,7 +184,7 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
if (shouldUpdate) {
|
||||
const remoteCipher = await this.apiService.getCipher(notification.id);
|
||||
if (remoteCipher != null) {
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
await this.cipherService.upsert(new CipherData(remoteCipher, userId));
|
||||
this.messagingService.send('syncedUpsertedCipher', { cipherId: notification.id });
|
||||
return this.syncCompleted(true);
|
||||
@@ -214,7 +203,7 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
|
||||
async syncDeleteCipher(notification: SyncCipherNotification): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
if (await this.stateService.getIsAuthenticated()) {
|
||||
await this.cipherService.delete(notification.id);
|
||||
this.messagingService.send('syncedDeletedCipher', { cipherId: notification.id });
|
||||
return this.syncCompleted(true);
|
||||
@@ -224,14 +213,14 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
|
||||
async syncUpsertSend(notification: SyncSendNotification, isEdit: boolean): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
if (await this.stateService.getIsAuthenticated()) {
|
||||
try {
|
||||
const localSend = await this.sendService.get(notification.id);
|
||||
if ((!isEdit && localSend == null) ||
|
||||
(isEdit && localSend != null && localSend.revisionDate < notification.revisionDate)) {
|
||||
const remoteSend = await this.apiService.getSend(notification.id);
|
||||
if (remoteSend != null) {
|
||||
const userId = await this.userService.getUserId();
|
||||
const userId = await this.stateService.getUserId();
|
||||
await this.sendService.upsert(new SendData(remoteSend, userId));
|
||||
this.messagingService.send('syncedUpsertedSend', { sendId: notification.id });
|
||||
return this.syncCompleted(true);
|
||||
@@ -246,7 +235,7 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
|
||||
async syncDeleteSend(notification: SyncSendNotification): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
if (await this.stateService.getIsAuthenticated()) {
|
||||
await this.sendService.delete(notification.id);
|
||||
this.messagingService.send('syncedDeletedSend', { sendId: notification.id });
|
||||
this.syncCompleted(true);
|
||||
@@ -286,7 +275,7 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
}
|
||||
|
||||
private async syncProfile(response: ProfileResponse) {
|
||||
const stamp = await this.userService.getSecurityStamp();
|
||||
const stamp = await this.stateService.getSecurityStamp();
|
||||
if (stamp != null && stamp !== response.securityStamp) {
|
||||
if (this.logoutCallback != null) {
|
||||
await this.logoutCallback(true);
|
||||
@@ -299,9 +288,9 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
await this.cryptoService.setEncPrivateKey(response.privateKey);
|
||||
await this.cryptoService.setProviderKeys(response.providers);
|
||||
await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations);
|
||||
await this.userService.setSecurityStamp(response.securityStamp);
|
||||
await this.userService.setEmailVerified(response.emailVerified);
|
||||
await this.userService.setForcePasswordReset(response.forcePasswordReset);
|
||||
await this.stateService.setSecurityStamp(response.securityStamp);
|
||||
await this.stateService.setEmailVerified(response.emailVerified);
|
||||
await this.stateService.setForcePasswordReset(response.forcePasswordReset);
|
||||
await this.keyConnectorService.setUsesKeyConnector(response.usesKeyConnector);
|
||||
|
||||
const organizations: { [id: string]: OrganizationData; } = {};
|
||||
@@ -322,8 +311,8 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
this.userService.replaceOrganizations(organizations),
|
||||
this.userService.replaceProviders(providers),
|
||||
this.organizationService.save(organizations),
|
||||
this.providerService.save(providers),
|
||||
]);
|
||||
|
||||
if (await this.keyConnectorService.userNeedsMigration()) {
|
||||
@@ -365,7 +354,7 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
return await this.sendService.replace(sends);
|
||||
}
|
||||
|
||||
private async syncSettings(userId: string, response: DomainsResponse) {
|
||||
private async syncSettings(response: DomainsResponse) {
|
||||
let eqDomains: string[][] = [];
|
||||
if (response != null && response.equivalentDomains != null) {
|
||||
eqDomains = eqDomains.concat(response.equivalentDomains);
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { MessagingService } from '../abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
import { SystemService as SystemServiceAbstraction } from '../abstractions/system.service';
|
||||
import { VaultTimeoutService } from '../abstractions/vaultTimeout.service';
|
||||
|
||||
import { ConstantsService } from './constants.service';
|
||||
|
||||
import { Utils } from '../misc/utils';
|
||||
|
||||
@@ -13,28 +10,27 @@ export class SystemService implements SystemServiceAbstraction {
|
||||
private clearClipboardTimeout: any = null;
|
||||
private clearClipboardTimeoutFunction: () => Promise<any> = null;
|
||||
|
||||
constructor(private storageService: StorageService, private vaultTimeoutService: VaultTimeoutService,
|
||||
private messagingService: MessagingService, private platformUtilsService: PlatformUtilsService,
|
||||
private reloadCallback: () => Promise<void> = null) {
|
||||
constructor(private messagingService: MessagingService, private platformUtilsService: PlatformUtilsService,
|
||||
private reloadCallback: () => Promise<void> = null, private stateService: StateService) {
|
||||
}
|
||||
|
||||
startProcessReload(): void {
|
||||
if (this.vaultTimeoutService.pinProtectedKey != null ||
|
||||
this.vaultTimeoutService.biometricLocked ||
|
||||
async startProcessReload(): Promise<void> {
|
||||
if (await this.stateService.getDecryptedPinProtected() != null ||
|
||||
await this.stateService.getBiometricLocked() ||
|
||||
this.reloadInterval != null) {
|
||||
return;
|
||||
}
|
||||
this.cancelProcessReload();
|
||||
this.reloadInterval = setInterval(async () => {
|
||||
let doRefresh = false;
|
||||
const lastActive = await this.storageService.get<number>(ConstantsService.lastActiveKey);
|
||||
const lastActive = await this.stateService.getLastActive();
|
||||
if (lastActive != null) {
|
||||
const diffSeconds = (new Date()).getTime() - lastActive;
|
||||
// Don't refresh if they are still active in the window
|
||||
doRefresh = diffSeconds >= 5000;
|
||||
}
|
||||
const biometricLockedFingerprintValidated =
|
||||
await this.storageService.get<boolean>(ConstantsService.biometricFingerprintValidated) && this.vaultTimeoutService.biometricLocked;
|
||||
await this.stateService.getBiometricFingerprintValidated() && await this.stateService.getBiometricLocked();
|
||||
if (doRefresh && !biometricLockedFingerprintValidated) {
|
||||
clearInterval(this.reloadInterval);
|
||||
this.reloadInterval = null;
|
||||
@@ -53,7 +49,7 @@ export class SystemService implements SystemServiceAbstraction {
|
||||
}
|
||||
}
|
||||
|
||||
clearClipboard(clipboardValue: string, timeoutMs: number = null): void {
|
||||
async clearClipboard(clipboardValue: string, timeoutMs: number = null): Promise<void> {
|
||||
if (this.clearClipboardTimeout != null) {
|
||||
clearTimeout(this.clearClipboardTimeout);
|
||||
this.clearClipboardTimeout = null;
|
||||
@@ -61,7 +57,7 @@ export class SystemService implements SystemServiceAbstraction {
|
||||
if (Utils.isNullOrWhitespace(clipboardValue)) {
|
||||
return;
|
||||
}
|
||||
this.storageService.get<number>(ConstantsService.clearClipboardKey).then(clearSeconds => {
|
||||
await this.stateService.getClearClipboard().then(clearSeconds => {
|
||||
if (clearSeconds == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,10 @@
|
||||
import { ConstantsService } from './constants.service';
|
||||
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
import { TokenService as TokenServiceAbstraction } from '../abstractions/token.service';
|
||||
|
||||
import { Utils } from '../misc/utils';
|
||||
|
||||
const Keys = {
|
||||
accessToken: 'accessToken',
|
||||
refreshToken: 'refreshToken',
|
||||
twoFactorTokenPrefix: 'twoFactorToken_',
|
||||
clientId: 'apikey_clientId',
|
||||
clientSecret: 'apikey_clientSecret',
|
||||
};
|
||||
|
||||
export class TokenService implements TokenServiceAbstraction {
|
||||
token: string;
|
||||
decodedToken: any;
|
||||
refreshToken: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
|
||||
constructor(private storageService: StorageService) {
|
||||
constructor(private stateService: StateService) {
|
||||
}
|
||||
|
||||
async setTokens(accessToken: string, refreshToken: string, clientIdClientSecret: [string, string]): Promise<any> {
|
||||
@@ -33,60 +17,44 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
}
|
||||
|
||||
async setClientId(clientId: string): Promise<any> {
|
||||
this.clientId = clientId;
|
||||
return this.storeTokenValue(Keys.clientId, clientId);
|
||||
if (await this.skipTokenStorage() || clientId == null) {
|
||||
return;
|
||||
}
|
||||
return await this.stateService.setApiKeyClientId(clientId);
|
||||
}
|
||||
|
||||
async getClientId(): Promise<string> {
|
||||
if (this.clientId != null) {
|
||||
return this.clientId;
|
||||
}
|
||||
|
||||
this.clientId = await this.storageService.get<string>(Keys.clientId);
|
||||
return this.clientId;
|
||||
return await this.stateService.getApiKeyClientId();
|
||||
}
|
||||
|
||||
async setClientSecret(clientSecret: string): Promise<any> {
|
||||
this.clientSecret = clientSecret;
|
||||
return this.storeTokenValue(Keys.clientSecret, clientSecret);
|
||||
if (await this.skipTokenStorage() || clientSecret == null) {
|
||||
return;
|
||||
}
|
||||
return await this.stateService.setApiKeyClientSecret(clientSecret);
|
||||
}
|
||||
|
||||
async getClientSecret(): Promise<string> {
|
||||
if (this.clientSecret != null) {
|
||||
return this.clientSecret;
|
||||
}
|
||||
|
||||
this.clientSecret = await this.storageService.get<string>(Keys.clientSecret);
|
||||
return this.clientSecret;
|
||||
return await this.stateService.getApiKeyClientSecret();
|
||||
}
|
||||
|
||||
async setToken(token: string): Promise<any> {
|
||||
this.token = token;
|
||||
this.decodedToken = null;
|
||||
return this.storeTokenValue(Keys.accessToken, token);
|
||||
async setToken(token: string): Promise<void> {
|
||||
await this.stateService.setAccessToken(token);
|
||||
}
|
||||
|
||||
async getToken(): Promise<string> {
|
||||
if (this.token != null) {
|
||||
return this.token;
|
||||
}
|
||||
|
||||
this.token = await this.storageService.get<string>(Keys.accessToken);
|
||||
return this.token;
|
||||
return await this.stateService.getAccessToken();
|
||||
}
|
||||
|
||||
async setRefreshToken(refreshToken: string): Promise<any> {
|
||||
this.refreshToken = refreshToken;
|
||||
return this.storeTokenValue(Keys.refreshToken, refreshToken);
|
||||
if (await this.skipTokenStorage()) {
|
||||
return;
|
||||
}
|
||||
return await this.stateService.setRefreshToken(refreshToken);
|
||||
}
|
||||
|
||||
async getRefreshToken(): Promise<string> {
|
||||
if (this.refreshToken != null) {
|
||||
return this.refreshToken;
|
||||
}
|
||||
|
||||
this.refreshToken = await this.storageService.get<string>(Keys.refreshToken);
|
||||
return this.refreshToken;
|
||||
return await this.stateService.getRefreshToken();
|
||||
}
|
||||
|
||||
async toggleTokens(): Promise<any> {
|
||||
@@ -94,16 +62,12 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
const refreshToken = await this.getRefreshToken();
|
||||
const clientId = await this.getClientId();
|
||||
const clientSecret = await this.getClientSecret();
|
||||
const timeout = await this.storageService.get(ConstantsService.vaultTimeoutKey);
|
||||
const action = await this.storageService.get(ConstantsService.vaultTimeoutActionKey);
|
||||
const timeout = await this.stateService.getVaultTimeout();
|
||||
const action = await this.stateService.getVaultTimeoutAction();
|
||||
|
||||
if ((timeout != null || timeout === 0) && action === 'logOut') {
|
||||
// if we have a vault timeout and the action is log out, reset tokens
|
||||
await this.clearToken();
|
||||
this.token = token;
|
||||
this.refreshToken = refreshToken;
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
return;
|
||||
}
|
||||
|
||||
await this.setToken(token);
|
||||
@@ -112,44 +76,41 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
await this.setClientSecret(clientSecret);
|
||||
}
|
||||
|
||||
setTwoFactorToken(token: string, email: string): Promise<any> {
|
||||
return this.storageService.save(Keys.twoFactorTokenPrefix + email, token);
|
||||
async setTwoFactorToken(token: string): Promise<any> {
|
||||
return await this.stateService.setTwoFactorToken(token);
|
||||
}
|
||||
|
||||
getTwoFactorToken(email: string): Promise<string> {
|
||||
return this.storageService.get<string>(Keys.twoFactorTokenPrefix + email);
|
||||
async getTwoFactorToken(): Promise<string> {
|
||||
return await this.stateService.getTwoFactorToken();
|
||||
}
|
||||
|
||||
clearTwoFactorToken(email: string): Promise<any> {
|
||||
return this.storageService.remove(Keys.twoFactorTokenPrefix + email);
|
||||
async clearTwoFactorToken(): Promise<any> {
|
||||
return await this.stateService.setTwoFactorToken(null);
|
||||
}
|
||||
|
||||
async clearToken(): Promise<any> {
|
||||
this.token = null;
|
||||
this.decodedToken = null;
|
||||
this.refreshToken = null;
|
||||
this.clientId = null;
|
||||
this.clientSecret = null;
|
||||
|
||||
await this.storageService.remove(Keys.accessToken);
|
||||
await this.storageService.remove(Keys.refreshToken);
|
||||
await this.storageService.remove(Keys.clientId);
|
||||
await this.storageService.remove(Keys.clientSecret);
|
||||
async clearToken(userId?: string): Promise<any> {
|
||||
await this.stateService.setAccessToken(null, { userId: userId });
|
||||
await this.stateService.setRefreshToken(null, { userId: userId });
|
||||
await this.stateService.setApiKeyClientId(null, { userId: userId });
|
||||
await this.stateService.setApiKeyClientSecret(null, { userId: userId });
|
||||
}
|
||||
|
||||
// jwthelper methods
|
||||
// ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js
|
||||
|
||||
decodeToken(): any {
|
||||
if (this.decodedToken) {
|
||||
return this.decodedToken;
|
||||
async decodeToken(token?: string): Promise<any> {
|
||||
const storedToken = await this.stateService.getDecodedToken();
|
||||
if (token === null && storedToken != null) {
|
||||
return storedToken;
|
||||
}
|
||||
|
||||
if (this.token == null) {
|
||||
token = token ?? await this.stateService.getAccessToken();
|
||||
|
||||
if (token == null) {
|
||||
throw new Error('Token not found.');
|
||||
}
|
||||
|
||||
const parts = this.token.split('.');
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 3) {
|
||||
throw new Error('JWT must have 3 parts');
|
||||
}
|
||||
@@ -159,12 +120,12 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
throw new Error('Cannot decode the token');
|
||||
}
|
||||
|
||||
this.decodedToken = JSON.parse(decoded);
|
||||
return this.decodedToken;
|
||||
const decodedToken = JSON.parse(decoded);
|
||||
return decodedToken;
|
||||
}
|
||||
|
||||
getTokenExpirationDate(): Date {
|
||||
const decoded = this.decodeToken();
|
||||
async getTokenExpirationDate(): Promise<Date> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.exp === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
@@ -174,8 +135,8 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
return d;
|
||||
}
|
||||
|
||||
tokenSecondsRemaining(offsetSeconds: number = 0): number {
|
||||
const d = this.getTokenExpirationDate();
|
||||
async tokenSecondsRemaining(offsetSeconds: number = 0): Promise<number> {
|
||||
const d = await this.getTokenExpirationDate();
|
||||
if (d == null) {
|
||||
return 0;
|
||||
}
|
||||
@@ -184,13 +145,13 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
return Math.round(msRemaining / 1000);
|
||||
}
|
||||
|
||||
tokenNeedsRefresh(minutes: number = 5): boolean {
|
||||
const sRemaining = this.tokenSecondsRemaining();
|
||||
async tokenNeedsRefresh(minutes: number = 5): Promise<boolean> {
|
||||
const sRemaining = await this.tokenSecondsRemaining();
|
||||
return sRemaining < (60 * minutes);
|
||||
}
|
||||
|
||||
getUserId(): string {
|
||||
const decoded = this.decodeToken();
|
||||
async getUserId(): Promise<string> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.sub === 'undefined') {
|
||||
throw new Error('No user id found');
|
||||
}
|
||||
@@ -198,8 +159,8 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
return decoded.sub as string;
|
||||
}
|
||||
|
||||
getEmail(): string {
|
||||
const decoded = this.decodeToken();
|
||||
async getEmail(): Promise<string> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.email === 'undefined') {
|
||||
throw new Error('No email found');
|
||||
}
|
||||
@@ -207,8 +168,8 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
return decoded.email as string;
|
||||
}
|
||||
|
||||
getEmailVerified(): boolean {
|
||||
const decoded = this.decodeToken();
|
||||
async getEmailVerified(): Promise<boolean> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.email_verified === 'undefined') {
|
||||
throw new Error('No email verification found');
|
||||
}
|
||||
@@ -216,8 +177,8 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
return decoded.email_verified as boolean;
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
const decoded = this.decodeToken();
|
||||
async getName(): Promise<string> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.name === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
@@ -225,8 +186,8 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
return decoded.name as string;
|
||||
}
|
||||
|
||||
getPremium(): boolean {
|
||||
const decoded = this.decodeToken();
|
||||
async getPremium(): Promise<boolean> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.premium === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
@@ -234,8 +195,8 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
return decoded.premium as boolean;
|
||||
}
|
||||
|
||||
getIssuer(): string {
|
||||
const decoded = this.decodeToken();
|
||||
async getIssuer(): Promise<string> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (typeof decoded.iss === 'undefined') {
|
||||
throw new Error('No issuer found');
|
||||
}
|
||||
@@ -243,8 +204,8 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
return decoded.iss as string;
|
||||
}
|
||||
|
||||
getIsExternal(): boolean {
|
||||
const decoded = this.decodeToken();
|
||||
async getIsExternal(): Promise<boolean> {
|
||||
const decoded = await this.decodeToken();
|
||||
if (!Array.isArray(decoded.amr)) {
|
||||
throw new Error('No amr found');
|
||||
}
|
||||
@@ -252,18 +213,9 @@ export class TokenService implements TokenServiceAbstraction {
|
||||
return decoded.amr.includes('external');
|
||||
}
|
||||
|
||||
private async storeTokenValue(key: string, value: string) {
|
||||
if (await this.skipTokenStorage()) {
|
||||
// if we have a vault timeout and the action is log out, don't store token
|
||||
return;
|
||||
}
|
||||
|
||||
return this.storageService.save(key, value);
|
||||
}
|
||||
|
||||
private async skipTokenStorage(): Promise<boolean> {
|
||||
const timeout = await this.storageService.get<number>(ConstantsService.vaultTimeoutKey);
|
||||
const action = await this.storageService.get<string>(ConstantsService.vaultTimeoutActionKey);
|
||||
const timeout = await this.stateService.getVaultTimeout();
|
||||
const action = await this.stateService.getVaultTimeoutAction();
|
||||
return timeout != null && action === 'logOut';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { ConstantsService } from './constants.service';
|
||||
|
||||
import { CryptoFunctionService } from '../abstractions/cryptoFunction.service';
|
||||
import { LogService } from '../abstractions/log.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
import { TotpService as TotpServiceAbstraction } from '../abstractions/totp.service';
|
||||
|
||||
import { Utils } from '../misc/utils';
|
||||
@@ -11,8 +9,8 @@ const B32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||
const SteamChars = '23456789BCDFGHJKMNPQRTVWXY';
|
||||
|
||||
export class TotpService implements TotpServiceAbstraction {
|
||||
constructor(private storageService: StorageService, private cryptoFunctionService: CryptoFunctionService,
|
||||
private logService: LogService) { }
|
||||
constructor(private cryptoFunctionService: CryptoFunctionService, private logService: LogService,
|
||||
private stateService: StateService) { }
|
||||
|
||||
async getCode(key: string): Promise<string> {
|
||||
if (key == null) {
|
||||
@@ -114,7 +112,7 @@ export class TotpService implements TotpServiceAbstraction {
|
||||
}
|
||||
|
||||
async isAutoCopyEnabled(): Promise<boolean> {
|
||||
return !(await this.storageService.get<boolean>(ConstantsService.disableAutoTotpCopyKey));
|
||||
return !(await this.stateService.getDisableAutoTotpCopy());
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { TokenService } from '../abstractions/token.service';
|
||||
import { UserService as UserServiceAbstraction } from '../abstractions/user.service';
|
||||
|
||||
import { OrganizationData } from '../models/data/organizationData';
|
||||
import { Organization } from '../models/domain/organization';
|
||||
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
|
||||
import { ProviderData } from '../models/data/providerData';
|
||||
import { Provider } from '../models/domain/provider';
|
||||
|
||||
const Keys = {
|
||||
userId: 'userId',
|
||||
userEmail: 'userEmail',
|
||||
stamp: 'securityStamp',
|
||||
kdf: 'kdf',
|
||||
kdfIterations: 'kdfIterations',
|
||||
organizationsPrefix: 'organizations_',
|
||||
providersPrefix: 'providers_',
|
||||
emailVerified: 'emailVerified',
|
||||
forcePasswordReset: 'forcePasswordReset',
|
||||
};
|
||||
|
||||
export class UserService implements UserServiceAbstraction {
|
||||
private userId: string;
|
||||
private email: string;
|
||||
private stamp: string;
|
||||
private kdf: KdfType;
|
||||
private kdfIterations: number;
|
||||
private emailVerified: boolean;
|
||||
private forcePasswordReset: boolean;
|
||||
|
||||
constructor(private tokenService: TokenService, private storageService: StorageService) { }
|
||||
|
||||
async setInformation(userId: string, email: string, kdf: KdfType, kdfIterations: number): Promise<any> {
|
||||
this.email = email;
|
||||
this.userId = userId;
|
||||
this.kdf = kdf;
|
||||
this.kdfIterations = kdfIterations;
|
||||
|
||||
await this.storageService.save(Keys.userEmail, email);
|
||||
await this.storageService.save(Keys.userId, userId);
|
||||
await this.storageService.save(Keys.kdf, kdf);
|
||||
await this.storageService.save(Keys.kdfIterations, kdfIterations);
|
||||
}
|
||||
|
||||
setSecurityStamp(stamp: string): Promise<any> {
|
||||
this.stamp = stamp;
|
||||
return this.storageService.save(Keys.stamp, stamp);
|
||||
}
|
||||
|
||||
setEmailVerified(emailVerified: boolean) {
|
||||
this.emailVerified = emailVerified;
|
||||
return this.storageService.save(Keys.emailVerified, emailVerified);
|
||||
}
|
||||
|
||||
setForcePasswordReset(forcePasswordReset: boolean) {
|
||||
this.forcePasswordReset = forcePasswordReset;
|
||||
return this.storageService.save(Keys.forcePasswordReset, forcePasswordReset);
|
||||
}
|
||||
|
||||
async getUserId(): Promise<string> {
|
||||
if (this.userId == null) {
|
||||
this.userId = await this.storageService.get<string>(Keys.userId);
|
||||
}
|
||||
return this.userId;
|
||||
}
|
||||
|
||||
async getEmail(): Promise<string> {
|
||||
if (this.email == null) {
|
||||
this.email = await this.storageService.get<string>(Keys.userEmail);
|
||||
}
|
||||
return this.email;
|
||||
}
|
||||
|
||||
async getSecurityStamp(): Promise<string> {
|
||||
if (this.stamp == null) {
|
||||
this.stamp = await this.storageService.get<string>(Keys.stamp);
|
||||
}
|
||||
return this.stamp;
|
||||
}
|
||||
|
||||
async getKdf(): Promise<KdfType> {
|
||||
if (this.kdf == null) {
|
||||
this.kdf = await this.storageService.get<KdfType>(Keys.kdf);
|
||||
}
|
||||
return this.kdf;
|
||||
}
|
||||
|
||||
async getKdfIterations(): Promise<number> {
|
||||
if (this.kdfIterations == null) {
|
||||
this.kdfIterations = await this.storageService.get<number>(Keys.kdfIterations);
|
||||
}
|
||||
return this.kdfIterations;
|
||||
}
|
||||
|
||||
async getEmailVerified(): Promise<boolean> {
|
||||
if (this.emailVerified == null) {
|
||||
this.emailVerified = await this.storageService.get<boolean>(Keys.emailVerified);
|
||||
}
|
||||
return this.emailVerified;
|
||||
}
|
||||
|
||||
async getForcePasswordReset(): Promise<boolean> {
|
||||
if (this.forcePasswordReset == null) {
|
||||
this.forcePasswordReset = await this.storageService.get<boolean>(Keys.forcePasswordReset);
|
||||
}
|
||||
return this.forcePasswordReset;
|
||||
}
|
||||
|
||||
async clear(): Promise<any> {
|
||||
const userId = await this.getUserId();
|
||||
|
||||
await this.storageService.remove(Keys.userId);
|
||||
await this.storageService.remove(Keys.userEmail);
|
||||
await this.storageService.remove(Keys.stamp);
|
||||
await this.storageService.remove(Keys.kdf);
|
||||
await this.storageService.remove(Keys.kdfIterations);
|
||||
await this.storageService.remove(Keys.forcePasswordReset);
|
||||
await this.clearOrganizations(userId);
|
||||
await this.clearProviders(userId);
|
||||
|
||||
this.userId = this.email = this.stamp = null;
|
||||
this.kdf = null;
|
||||
this.kdfIterations = null;
|
||||
}
|
||||
|
||||
async isAuthenticated(): Promise<boolean> {
|
||||
const token = await this.tokenService.getToken();
|
||||
if (token == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const userId = await this.getUserId();
|
||||
return userId != null;
|
||||
}
|
||||
|
||||
async canAccessPremium(): Promise<boolean> {
|
||||
const authed = await this.isAuthenticated();
|
||||
if (!authed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const tokenPremium = this.tokenService.getPremium();
|
||||
if (tokenPremium) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const orgs = await this.getAllOrganizations();
|
||||
for (let i = 0; i < orgs.length; i++) {
|
||||
if (orgs[i].usersGetPremium && orgs[i].enabled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async canManageSponsorships(): Promise<boolean> {
|
||||
const orgs = await this.getAllOrganizations();
|
||||
return orgs.some(o => o.familySponsorshipAvailable || o.familySponsorshipFriendlyName !== null);
|
||||
}
|
||||
|
||||
async getOrganization(id: string): Promise<Organization> {
|
||||
const userId = await this.getUserId();
|
||||
const organizations = await this.storageService.get<{ [id: string]: OrganizationData; }>(
|
||||
Keys.organizationsPrefix + userId);
|
||||
if (organizations == null || !organizations.hasOwnProperty(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Organization(organizations[id]);
|
||||
}
|
||||
|
||||
async getOrganizationByIdentifier(identifier: string): Promise<Organization> {
|
||||
const organizations = await this.getAllOrganizations();
|
||||
if (organizations == null || organizations.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return organizations.find(o => o.identifier === identifier);
|
||||
}
|
||||
|
||||
async getAllOrganizations(): Promise<Organization[]> {
|
||||
const userId = await this.getUserId();
|
||||
const organizations = await this.storageService.get<{ [id: string]: OrganizationData; }>(
|
||||
Keys.organizationsPrefix + userId);
|
||||
const response: Organization[] = [];
|
||||
for (const id in organizations) {
|
||||
if (organizations.hasOwnProperty(id) && !organizations[id].isProviderUser) {
|
||||
response.push(new Organization(organizations[id]));
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async replaceOrganizations(organizations: { [id: string]: OrganizationData; }): Promise<any> {
|
||||
const userId = await this.getUserId();
|
||||
await this.storageService.save(Keys.organizationsPrefix + userId, organizations);
|
||||
}
|
||||
|
||||
async clearOrganizations(userId: string): Promise<any> {
|
||||
await this.storageService.remove(Keys.organizationsPrefix + userId);
|
||||
}
|
||||
|
||||
async getProvider(id: string): Promise<Provider> {
|
||||
const userId = await this.getUserId();
|
||||
const providers = await this.storageService.get<{ [id: string]: ProviderData; }>(
|
||||
Keys.providersPrefix + userId);
|
||||
if (providers == null || !providers.hasOwnProperty(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Provider(providers[id]);
|
||||
}
|
||||
|
||||
async getAllProviders(): Promise<Provider[]> {
|
||||
const userId = await this.getUserId();
|
||||
const providers = await this.storageService.get<{ [id: string]: ProviderData; }>(
|
||||
Keys.providersPrefix + userId);
|
||||
const response: Provider[] = [];
|
||||
for (const id in providers) {
|
||||
if (providers.hasOwnProperty(id)) {
|
||||
response.push(new Provider(providers[id]));
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async replaceProviders(providers: { [id: string]: ProviderData; }): Promise<any> {
|
||||
const userId = await this.getUserId();
|
||||
await this.storageService.save(Keys.providersPrefix + userId, providers);
|
||||
}
|
||||
|
||||
async clearProviders(userId: string): Promise<any> {
|
||||
await this.storageService.remove(Keys.providersPrefix + userId);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import { ConstantsService } from './constants.service';
|
||||
|
||||
import { CipherService } from '../abstractions/cipher.service';
|
||||
import { CollectionService } from '../abstractions/collection.service';
|
||||
import { CryptoService } from '../abstractions/crypto.service';
|
||||
@@ -9,29 +7,31 @@ import { MessagingService } from '../abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
||||
import { PolicyService } from '../abstractions/policy.service';
|
||||
import { SearchService } from '../abstractions/search.service';
|
||||
import { StorageService } from '../abstractions/storage.service';
|
||||
import { StateService } from '../abstractions/state.service';
|
||||
import { TokenService } from '../abstractions/token.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from '../abstractions/vaultTimeout.service';
|
||||
import { KeySuffixOptions } from '../enums/keySuffixOptions';
|
||||
|
||||
import { PolicyType } from '../enums/policyType';
|
||||
import { EncString } from '../models/domain/encString';
|
||||
|
||||
export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
||||
pinProtectedKey: EncString = null;
|
||||
biometricLocked: boolean = true;
|
||||
everBeenUnlocked: boolean = false;
|
||||
|
||||
private inited = false;
|
||||
|
||||
constructor(private cipherService: CipherService, private folderService: FolderService,
|
||||
private collectionService: CollectionService, private cryptoService: CryptoService,
|
||||
protected platformUtilsService: PlatformUtilsService, private storageService: StorageService,
|
||||
private messagingService: MessagingService, private searchService: SearchService,
|
||||
private userService: UserService, private tokenService: TokenService, private policyService: PolicyService,
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private folderService: FolderService,
|
||||
private collectionService: CollectionService,
|
||||
private cryptoService: CryptoService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
private messagingService: MessagingService,
|
||||
private searchService: SearchService,
|
||||
private tokenService: TokenService,
|
||||
private policyService: PolicyService,
|
||||
private keyConnectorService: KeyConnectorService,
|
||||
private lockedCallback: () => Promise<void> = null, private loggedOutCallback: () => Promise<void> = null) {
|
||||
}
|
||||
private stateService: StateService,
|
||||
private lockedCallback: () => Promise<void> = null,
|
||||
private loggedOutCallback: (userId?: string) => Promise<void> = null
|
||||
) {}
|
||||
|
||||
init(checkOnInterval: boolean) {
|
||||
if (this.inited) {
|
||||
@@ -50,110 +50,96 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
||||
}
|
||||
|
||||
// Keys aren't stored for a device that is locked or logged out.
|
||||
async isLocked(): Promise<boolean> {
|
||||
// Handle never lock startup situation
|
||||
if (await this.cryptoService.hasKeyStored('auto') && !this.everBeenUnlocked) {
|
||||
await this.cryptoService.getKey('auto');
|
||||
async isLocked(userId?: string): Promise<boolean> {
|
||||
const neverLock = await this.cryptoService.hasKeyStored(KeySuffixOptions.Auto, userId) &&
|
||||
!(await this.stateService.getEverBeenUnlocked({ userId: userId }));
|
||||
if (neverLock) {
|
||||
// TODO: This also _sets_ the key so when we check memory in the next line it finds a key.
|
||||
// We should refactor here.
|
||||
await this.cryptoService.getKey(KeySuffixOptions.Auto, userId);
|
||||
}
|
||||
|
||||
return !this.cryptoService.hasKeyInMemory();
|
||||
return !(await this.cryptoService.hasKeyInMemory(userId));
|
||||
}
|
||||
|
||||
async checkVaultTimeout(): Promise<void> {
|
||||
if (await this.platformUtilsService.isViewOpen()) {
|
||||
// Do not lock
|
||||
return;
|
||||
}
|
||||
|
||||
// "is logged out check" - similar to isLocked, below
|
||||
const authed = await this.userService.isAuthenticated();
|
||||
if (!authed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await this.isLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const vaultTimeout = await this.getVaultTimeout();
|
||||
if (vaultTimeout == null || vaultTimeout < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastActive = await this.storageService.get<number>(ConstantsService.lastActiveKey);
|
||||
if (lastActive == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const vaultTimeoutSeconds = vaultTimeout * 60;
|
||||
const diffSeconds = ((new Date()).getTime() - lastActive) / 1000;
|
||||
if (diffSeconds >= vaultTimeoutSeconds) {
|
||||
// Pivot based on the saved vault timeout action
|
||||
const timeoutAction = await this.storageService.get<string>(ConstantsService.vaultTimeoutActionKey);
|
||||
timeoutAction === 'logOut' ? await this.logOut() : await this.lock(true);
|
||||
for (const userId in this.stateService.accounts.getValue()) {
|
||||
if (userId != null && await this.shouldLock(userId)) {
|
||||
await this.executeTimeoutAction(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async lock(allowSoftLock = false): Promise<void> {
|
||||
const authed = await this.userService.isAuthenticated();
|
||||
async lock(allowSoftLock = false, userId?: string): Promise<void> {
|
||||
const authed = await this.stateService.getIsAuthenticated({ userId: userId });
|
||||
if (!authed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await this.keyConnectorService.getUsesKeyConnector()) {
|
||||
const pinSet = await this.isPinLockSet();
|
||||
const pinLock = (pinSet[0] && this.pinProtectedKey != null) || pinSet[1];
|
||||
const pinLock = (pinSet[0] && await this.stateService.getDecryptedPinProtected() != null) || pinSet[1];
|
||||
|
||||
if (!pinLock && !await this.isBiometricLockSet()) {
|
||||
await this.logOut();
|
||||
}
|
||||
}
|
||||
|
||||
this.biometricLocked = true;
|
||||
this.everBeenUnlocked = true;
|
||||
await this.cryptoService.clearKey(false);
|
||||
await this.cryptoService.clearOrgKeys(true);
|
||||
await this.cryptoService.clearKeyPair(true);
|
||||
await this.cryptoService.clearEncKey(true);
|
||||
if (userId == null || userId === await this.stateService.getUserId()) {
|
||||
this.searchService.clearIndex();
|
||||
}
|
||||
|
||||
await this.stateService.setEverBeenUnlocked(true, { userId: userId });
|
||||
await this.stateService.setBiometricLocked(true, { userId: userId });
|
||||
|
||||
await this.cryptoService.clearKey(false, userId);
|
||||
await this.cryptoService.clearOrgKeys(true, userId);
|
||||
await this.cryptoService.clearKeyPair(true, userId);
|
||||
await this.cryptoService.clearEncKey(true, userId);
|
||||
|
||||
await this.folderService.clearCache(userId);
|
||||
await this.cipherService.clearCache(userId);
|
||||
await this.collectionService.clearCache(userId);
|
||||
|
||||
this.messagingService.send('locked', { userId: userId });
|
||||
|
||||
this.folderService.clearCache();
|
||||
this.cipherService.clearCache();
|
||||
this.collectionService.clearCache();
|
||||
this.searchService.clearIndex();
|
||||
this.messagingService.send('locked');
|
||||
if (this.lockedCallback != null) {
|
||||
await this.lockedCallback();
|
||||
}
|
||||
}
|
||||
|
||||
async logOut(): Promise<void> {
|
||||
async logOut(userId?: string): Promise<void> {
|
||||
if (this.loggedOutCallback != null) {
|
||||
await this.loggedOutCallback();
|
||||
await this.loggedOutCallback(userId);
|
||||
}
|
||||
}
|
||||
|
||||
async setVaultTimeoutOptions(timeout: number, action: string): Promise<void> {
|
||||
await this.storageService.save(ConstantsService.vaultTimeoutKey, timeout);
|
||||
await this.storageService.save(ConstantsService.vaultTimeoutActionKey, action);
|
||||
await this.stateService.setVaultTimeout(timeout);
|
||||
await this.stateService.setVaultTimeoutAction(action);
|
||||
await this.cryptoService.toggleKey();
|
||||
await this.tokenService.toggleTokens();
|
||||
}
|
||||
|
||||
async isPinLockSet(): Promise<[boolean, boolean]> {
|
||||
const protectedPin = await this.storageService.get<string>(ConstantsService.protectedPin);
|
||||
const pinProtectedKey = await this.storageService.get<string>(ConstantsService.pinProtectedKey);
|
||||
const protectedPin = await this.stateService.getProtectedPin();
|
||||
const pinProtectedKey = await this.stateService.getEncryptedPinProtected();
|
||||
return [protectedPin != null, pinProtectedKey != null];
|
||||
}
|
||||
|
||||
async isBiometricLockSet(): Promise<boolean> {
|
||||
return await this.storageService.get<boolean>(ConstantsService.biometricUnlockKey);
|
||||
return await this.stateService.getBiometricUnlock();
|
||||
}
|
||||
|
||||
async getVaultTimeout(): Promise<number> {
|
||||
const vaultTimeout = await this.storageService.get<number>(ConstantsService.vaultTimeoutKey);
|
||||
async getVaultTimeout(userId?: string): Promise<number> {
|
||||
const vaultTimeout = await this.stateService.getVaultTimeout( { userId: userId } );
|
||||
|
||||
if (await this.policyService.policyAppliesToUser(PolicyType.MaximumVaultTimeout)) {
|
||||
const policy = await this.policyService.getAll(PolicyType.MaximumVaultTimeout);
|
||||
if (await this.policyService.policyAppliesToUser(PolicyType.MaximumVaultTimeout, null, userId)) {
|
||||
const policy = await this.policyService.getAll(PolicyType.MaximumVaultTimeout, userId);
|
||||
// Remove negative values, and ensure it's smaller than maximum allowed value according to policy
|
||||
let timeout = Math.min(vaultTimeout, policy[0].data.minutes);
|
||||
|
||||
@@ -163,7 +149,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
||||
|
||||
// We really shouldn't need to set the value here, but multiple services relies on this value being correct.
|
||||
if (vaultTimeout !== timeout) {
|
||||
await this.storageService.save(ConstantsService.vaultTimeoutKey, timeout);
|
||||
await this.stateService.setVaultTimeout(timeout, { userId: userId });
|
||||
}
|
||||
|
||||
return timeout;
|
||||
@@ -172,9 +158,42 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
||||
return vaultTimeout;
|
||||
}
|
||||
|
||||
clear(): Promise<any> {
|
||||
this.everBeenUnlocked = false;
|
||||
this.pinProtectedKey = null;
|
||||
return this.storageService.remove(ConstantsService.protectedPin);
|
||||
async clear(userId?: string): Promise<void> {
|
||||
await this.stateService.setEverBeenUnlocked(false, { userId: userId });
|
||||
await this.stateService.setDecryptedPinProtected(null, { userId: userId });
|
||||
await this.stateService.setProtectedPin(null, { userId: userId });
|
||||
}
|
||||
|
||||
private async isLoggedOut(userId?: string): Promise<boolean> {
|
||||
return !(await this.stateService.getIsAuthenticated({ userId: userId }));
|
||||
}
|
||||
|
||||
private async shouldLock(userId: string): Promise<boolean> {
|
||||
if (await this.isLoggedOut(userId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await this.isLocked(userId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const vaultTimeout = await this.getVaultTimeout(userId);
|
||||
if (vaultTimeout == null || vaultTimeout < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const lastActive = await this.stateService.getLastActive({ userId: userId });
|
||||
if (lastActive == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const vaultTimeoutSeconds = vaultTimeout * 60;
|
||||
const diffSeconds = ((new Date()).getTime() - lastActive) / 1000;
|
||||
return diffSeconds >= vaultTimeoutSeconds;
|
||||
}
|
||||
|
||||
private async executeTimeoutAction(userId: string): Promise<void> {
|
||||
const timeoutAction = await this.stateService.getVaultTimeoutAction({ userId: userId });
|
||||
timeoutAction === 'logOut' ? await this.logOut() : await this.lock(true, userId);
|
||||
}
|
||||
}
|
||||
|
||||
89
electron/package-lock.json
generated
89
electron/package-lock.json
generated
@@ -11,20 +11,21 @@
|
||||
"dependencies": {
|
||||
"@bitwarden/jslib-common": "file:../common",
|
||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
||||
"electron": "14.2.0",
|
||||
"electron": "16.0.2",
|
||||
"electron-log": "4.4.1",
|
||||
"electron-store": "8.0.1",
|
||||
"electron-updater": "4.3.9",
|
||||
"electron-updater": "4.6.1",
|
||||
"forcefocus": "^1.1.0",
|
||||
"keytar": "7.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.17.1",
|
||||
"@types/node": "^16.11.12",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "4.3.5"
|
||||
}
|
||||
},
|
||||
"../common": {
|
||||
"name": "@bitwarden/jslib-common",
|
||||
"version": "0.0.0",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
@@ -41,7 +42,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/node": "^14.17.1",
|
||||
"@types/node": "^16.11.12",
|
||||
"@types/node-forge": "^0.9.7",
|
||||
"@types/papaparse": "^5.2.5",
|
||||
"@types/tldjs": "^2.3.0",
|
||||
@@ -104,9 +105,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "14.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.0.tgz",
|
||||
"integrity": "sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ=="
|
||||
"version": "16.11.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
|
||||
"integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.3.9",
|
||||
@@ -288,9 +290,9 @@
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"node_modules/builder-util-runtime": {
|
||||
"version": "8.7.5",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.5.tgz",
|
||||
"integrity": "sha512-fgUFHKtMNjdvH6PDRFntdIGUPgwZ69sXsAqEulCtoiqgWes5agrMq/Ud274zjJRTbckYh2PHh8/1CpFc6dpsbQ==",
|
||||
"version": "8.9.1",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.9.1.tgz",
|
||||
"integrity": "sha512-c8a8J3wK6BIVLW7ls+7TRK9igspTbzWmUqxFbgK0m40Ggm6efUbxtWVCGIjc+dtchyr5qAMAUL6iEGRdS/6vwg==",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.2",
|
||||
"sax": "^1.2.4"
|
||||
@@ -544,12 +546,12 @@
|
||||
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.2.0.tgz",
|
||||
"integrity": "sha512-6CmAv1P0xcwK3FQOSA27fHI36/wctSFVgj46VODn56srXXQWeolkK1VzeAFNE613iAuuH9jJdHvE3gz+c7XkNA==",
|
||||
"version": "16.0.2",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-16.0.2.tgz",
|
||||
"integrity": "sha512-kT746yVMztrP4BbT3nrFNcUcfgFu2yelUw6TWBVTy0pju+fBISaqcvoiMrq+8U0vRpoXSu2MJYygOf4T0Det7g==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@electron/get": "^1.0.1",
|
||||
"@electron/get": "^1.13.0",
|
||||
"@types/node": "^14.6.2",
|
||||
"extract-zip": "^1.0.3"
|
||||
},
|
||||
@@ -578,15 +580,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-updater": {
|
||||
"version": "4.3.9",
|
||||
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.3.9.tgz",
|
||||
"integrity": "sha512-LCNfedSwZfS4Hza+pDyPR05LqHtGorCStaBgVpRnfKxOlZcvpYEX0AbMeH5XUtbtGRoH2V8osbbf2qKPNb7AsA==",
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.6.1.tgz",
|
||||
"integrity": "sha512-YsU1mHqXLrXXmBMsxhxy24PrbaB8rnpZDPmFa2gOkTYk/Ch13+R0fjsRSpPYvqtskVVY0ux8fu+HnUkVkqc7og==",
|
||||
"dependencies": {
|
||||
"@types/semver": "^7.3.5",
|
||||
"builder-util-runtime": "8.7.5",
|
||||
"@types/semver": "^7.3.6",
|
||||
"builder-util-runtime": "8.9.1",
|
||||
"fs-extra": "^10.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lazy-val": "^1.0.4",
|
||||
"lazy-val": "^1.0.5",
|
||||
"lodash.escaperegexp": "^4.1.2",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"semver": "^7.3.5"
|
||||
@@ -638,6 +640,11 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron/node_modules/@types/node": {
|
||||
"version": "14.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.0.tgz",
|
||||
"integrity": "sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ=="
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
@@ -1887,7 +1894,7 @@
|
||||
"@microsoft/signalr": "5.0.10",
|
||||
"@microsoft/signalr-protocol-msgpack": "5.0.10",
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/node": "^14.17.1",
|
||||
"@types/node": "^16.11.12",
|
||||
"@types/node-forge": "^0.9.7",
|
||||
"@types/papaparse": "^5.2.5",
|
||||
"@types/tldjs": "^2.3.0",
|
||||
@@ -1942,9 +1949,10 @@
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.0.tgz",
|
||||
"integrity": "sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ=="
|
||||
"version": "16.11.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz",
|
||||
"integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/semver": {
|
||||
"version": "7.3.9",
|
||||
@@ -2076,9 +2084,9 @@
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"builder-util-runtime": {
|
||||
"version": "8.7.5",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.5.tgz",
|
||||
"integrity": "sha512-fgUFHKtMNjdvH6PDRFntdIGUPgwZ69sXsAqEulCtoiqgWes5agrMq/Ud274zjJRTbckYh2PHh8/1CpFc6dpsbQ==",
|
||||
"version": "8.9.1",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.9.1.tgz",
|
||||
"integrity": "sha512-c8a8J3wK6BIVLW7ls+7TRK9igspTbzWmUqxFbgK0m40Ggm6efUbxtWVCGIjc+dtchyr5qAMAUL6iEGRdS/6vwg==",
|
||||
"requires": {
|
||||
"debug": "^4.3.2",
|
||||
"sax": "^1.2.4"
|
||||
@@ -2268,13 +2276,20 @@
|
||||
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
|
||||
},
|
||||
"electron": {
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.2.0.tgz",
|
||||
"integrity": "sha512-6CmAv1P0xcwK3FQOSA27fHI36/wctSFVgj46VODn56srXXQWeolkK1VzeAFNE613iAuuH9jJdHvE3gz+c7XkNA==",
|
||||
"version": "16.0.2",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-16.0.2.tgz",
|
||||
"integrity": "sha512-kT746yVMztrP4BbT3nrFNcUcfgFu2yelUw6TWBVTy0pju+fBISaqcvoiMrq+8U0vRpoXSu2MJYygOf4T0Det7g==",
|
||||
"requires": {
|
||||
"@electron/get": "^1.0.1",
|
||||
"@electron/get": "^1.13.0",
|
||||
"@types/node": "^14.6.2",
|
||||
"extract-zip": "^1.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "14.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.0.tgz",
|
||||
"integrity": "sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"electron-log": {
|
||||
@@ -2292,15 +2307,15 @@
|
||||
}
|
||||
},
|
||||
"electron-updater": {
|
||||
"version": "4.3.9",
|
||||
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.3.9.tgz",
|
||||
"integrity": "sha512-LCNfedSwZfS4Hza+pDyPR05LqHtGorCStaBgVpRnfKxOlZcvpYEX0AbMeH5XUtbtGRoH2V8osbbf2qKPNb7AsA==",
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.6.1.tgz",
|
||||
"integrity": "sha512-YsU1mHqXLrXXmBMsxhxy24PrbaB8rnpZDPmFa2gOkTYk/Ch13+R0fjsRSpPYvqtskVVY0ux8fu+HnUkVkqc7og==",
|
||||
"requires": {
|
||||
"@types/semver": "^7.3.5",
|
||||
"builder-util-runtime": "8.7.5",
|
||||
"@types/semver": "^7.3.6",
|
||||
"builder-util-runtime": "8.9.1",
|
||||
"fs-extra": "^10.0.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lazy-val": "^1.0.4",
|
||||
"lazy-val": "^1.0.5",
|
||||
"lodash.escaperegexp": "^4.1.2",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"semver": "^7.3.5"
|
||||
|
||||
@@ -22,17 +22,17 @@
|
||||
"test:node": " "
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.17.1",
|
||||
"@types/node": "^16.11.12",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bitwarden/jslib-common": "file:../common",
|
||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
||||
"electron": "14.2.0",
|
||||
"electron": "16.0.2",
|
||||
"electron-log": "4.4.1",
|
||||
"electron-store": "8.0.1",
|
||||
"electron-updater": "4.3.9",
|
||||
"electron-updater": "4.6.1",
|
||||
"forcefocus": "^1.1.0",
|
||||
"keytar": "7.7.0"
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import { ipcMain, systemPreferences } from 'electron';
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
import { BiometricMain } from 'jslib-common/abstractions/biometric.main';
|
||||
import { ElectronConstants } from './electronConstants';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
export default class BiometricDarwinMain implements BiometricMain {
|
||||
isError: boolean = false;
|
||||
|
||||
constructor(private storageService: StorageService, private i18nservice: I18nService) {}
|
||||
constructor(private i18nservice: I18nService, private stateService: StateService) {}
|
||||
|
||||
async init() {
|
||||
this.storageService.save(ElectronConstants.enableBiometric, await this.supportsBiometric());
|
||||
this.storageService.save(ConstantsService.biometricText, 'unlockWithTouchId');
|
||||
this.storageService.save(ElectronConstants.noAutoPromptBiometricsText, 'noAutoPromptTouchId');
|
||||
await this.stateService.setEnableBiometric(await this.supportsBiometric());
|
||||
await this.stateService.setBiometricText('unlockWithTouchId');
|
||||
await this.stateService.setNoAutoPromptBiometricsText('noAutoPromptTouchId');
|
||||
|
||||
ipcMain.on('biometric', async (event: any, message: any) => {
|
||||
event.returnValue = await this.authenticateBiometric();
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import forceFocus from 'forcefocus';
|
||||
|
||||
import { ElectronConstants } from './electronConstants';
|
||||
import { WindowMain } from './window.main';
|
||||
|
||||
import { BiometricMain } from 'jslib-common/abstractions/biometric.main';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
export default class BiometricWindowsMain implements BiometricMain {
|
||||
isError: boolean = false;
|
||||
|
||||
private windowsSecurityCredentialsUiModule: any;
|
||||
|
||||
constructor(private storageService: StorageService, private i18nservice: I18nService, private windowMain: WindowMain,
|
||||
private logService: LogService) { }
|
||||
constructor(private i18nservice: I18nService, private windowMain: WindowMain,
|
||||
private stateService: StateService, private logService: LogService) { }
|
||||
|
||||
async init() {
|
||||
this.windowsSecurityCredentialsUiModule = this.getWindowsSecurityCredentialsUiModule();
|
||||
@@ -27,9 +25,9 @@ export default class BiometricWindowsMain implements BiometricMain {
|
||||
// store error state so we can let the user know on the settings page
|
||||
this.isError = true;
|
||||
}
|
||||
this.storageService.save(ElectronConstants.enableBiometric, supportsBiometric);
|
||||
this.storageService.save(ConstantsService.biometricText, 'unlockWithWindowsHello');
|
||||
this.storageService.save(ElectronConstants.noAutoPromptBiometricsText, 'noAutoPromptWindowsHello');
|
||||
await this.stateService.setEnableBiometric(supportsBiometric);
|
||||
await this.stateService.setBiometricText('unlockWithWindowsHello');
|
||||
await this.stateService.setNoAutoPromptBiometricsText('noAutoPromptWindowsHello');
|
||||
|
||||
ipcMain.on('biometric', async (event: any, message: any) => {
|
||||
event.returnValue = await this.authenticateBiometric();
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
export class ElectronConstants {
|
||||
static readonly enableMinimizeToTrayKey: string = 'enableMinimizeToTray';
|
||||
static readonly enableCloseToTrayKey: string = 'enableCloseToTray';
|
||||
static readonly enableTrayKey: string = 'enableTray';
|
||||
static readonly enableStartToTrayKey: string = 'enableStartToTrayKey';
|
||||
static readonly enableAlwaysOnTopKey: string = 'enableAlwaysOnTopKey';
|
||||
static readonly minimizeOnCopyToClipboardKey: string = 'minimizeOnCopyToClipboardKey';
|
||||
static readonly enableBiometric: string = 'enabledBiometric';
|
||||
static readonly enableBrowserIntegration: string = 'enableBrowserIntegration';
|
||||
static readonly enableBrowserIntegrationFingerprint: string = 'enableBrowserIntegrationFingerprint';
|
||||
static readonly alwaysShowDock: string = 'alwaysShowDock';
|
||||
static readonly openAtLogin: string = 'openAtLogin';
|
||||
static readonly noAutoPromptBiometricsText: string = 'noAutoPromptBiometricsText';
|
||||
}
|
||||
@@ -1,16 +1,19 @@
|
||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { KeySuffixOptions, StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
import { CryptoService } from 'jslib-common/services/crypto.service';
|
||||
|
||||
import { KeySuffixOptions } from 'jslib-common/enums/keySuffixOptions';
|
||||
import { StorageLocation } from 'jslib-common/enums/storageLocation';
|
||||
import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
|
||||
import { CryptoService, Keys } from 'jslib-common/services/crypto.service';
|
||||
|
||||
export class ElectronCryptoService extends CryptoService {
|
||||
|
||||
constructor(storageService: StorageService, secureStorageService: StorageService,
|
||||
cryptoFunctionService: CryptoFunctionService, platformUtilService: PlatformUtilsService,
|
||||
logService: LogService) {
|
||||
super(storageService, secureStorageService, cryptoFunctionService, platformUtilService, logService);
|
||||
constructor(cryptoFunctionService: CryptoFunctionService, platformUtilService: PlatformUtilsService,
|
||||
logService: LogService, stateService: StateService) {
|
||||
super(cryptoFunctionService, platformUtilService, logService, stateService);
|
||||
}
|
||||
|
||||
async hasKeyStored(keySuffix: KeySuffixOptions): Promise<boolean> {
|
||||
@@ -18,17 +21,17 @@ export class ElectronCryptoService extends CryptoService {
|
||||
return super.hasKeyStored(keySuffix);
|
||||
}
|
||||
|
||||
protected async storeKey(key: SymmetricCryptoKey) {
|
||||
if (await this.shouldStoreKey('auto')) {
|
||||
await this.secureStorageService.save(Keys.key, key.keyB64, { keySuffix: 'auto' });
|
||||
protected async storeKey(key: SymmetricCryptoKey, userId?: string) {
|
||||
if (await this.shouldStoreKey(KeySuffixOptions.Auto, userId)) {
|
||||
await this.stateService.setCryptoMasterKeyAuto(key.keyB64, { userId: userId });
|
||||
} else {
|
||||
this.clearStoredKey('auto');
|
||||
this.clearStoredKey(KeySuffixOptions.Auto);
|
||||
}
|
||||
|
||||
if (await this.shouldStoreKey('biometric')) {
|
||||
await this.secureStorageService.save(Keys.key, key.keyB64, { keySuffix: 'biometric' });
|
||||
if (await this.shouldStoreKey(KeySuffixOptions.Biometric, userId)) {
|
||||
await this.stateService.setCryptoMasterKeyBiometric(key.keyB64, { userId: userId });
|
||||
} else {
|
||||
this.clearStoredKey('biometric');
|
||||
this.clearStoredKey(KeySuffixOptions.Biometric);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,24 +46,24 @@ export class ElectronCryptoService extends CryptoService {
|
||||
*/
|
||||
private async upgradeSecurelyStoredKey() {
|
||||
// attempt key upgrade, but if we fail just delete it. Keys will be stored property upon unlock anyway.
|
||||
const key = await this.secureStorageService.get<string>(Keys.key);
|
||||
const key = await this.stateService.getCryptoMasterKeyB64();
|
||||
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (await this.shouldStoreKey('auto')) {
|
||||
await this.secureStorageService.save(Keys.key, key, { keySuffix: 'auto' });
|
||||
if (await this.shouldStoreKey(KeySuffixOptions.Auto)) {
|
||||
await this.stateService.setCryptoMasterKeyAuto(key);
|
||||
}
|
||||
if (await this.shouldStoreKey('biometric')) {
|
||||
await this.secureStorageService.save(Keys.key, key, { keySuffix: 'biometric' });
|
||||
if (await this.shouldStoreKey(KeySuffixOptions.Biometric)) {
|
||||
await this.stateService.setCryptoMasterKeyBiometric(key);
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.error(`Encountered error while upgrading obsolete Bitwarden secure storage item:`);
|
||||
this.logService.error(e);
|
||||
}
|
||||
|
||||
await this.secureStorageService.remove(Keys.key);
|
||||
await this.stateService.setCryptoMasterKeyB64(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,7 @@ import { ThemeType } from 'jslib-common/enums/themeType';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
|
||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||
|
||||
import { ElectronConstants } from '../electronConstants';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
identityClientId: string;
|
||||
@@ -27,7 +23,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
private deviceCache: DeviceType = null;
|
||||
|
||||
constructor(protected i18nService: I18nService, private messagingService: MessagingService,
|
||||
private isDesktopApp: boolean, private storageService: StorageService) {
|
||||
private isDesktopApp: boolean, private stateService: StateService) {
|
||||
this.identityClientId = isDesktopApp ? 'desktop' : 'connector';
|
||||
}
|
||||
|
||||
@@ -178,8 +174,8 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
return Promise.resolve(clipboard.readText(type));
|
||||
}
|
||||
|
||||
supportsBiometric(): Promise<boolean> {
|
||||
return this.storageService.get(ElectronConstants.enableBiometric);
|
||||
async supportsBiometric(): Promise<boolean> {
|
||||
return await this.stateService.getEnableBiometric();
|
||||
}
|
||||
|
||||
authenticateBiometric(): Promise<boolean> {
|
||||
@@ -200,7 +196,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
}
|
||||
|
||||
async getEffectiveTheme() {
|
||||
const theme = await this.storageService.get<ThemeType>(ConstantsService.themeKey);
|
||||
const theme = await this.stateService.getTheme();
|
||||
if (theme == null || theme === ThemeType.System) {
|
||||
return this.getDefaultSystemTheme();
|
||||
} else {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import { StorageService, StorageServiceOptions } from 'jslib-common/abstractions/storage.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
|
||||
import { StorageOptions } from 'jslib-common/models/domain/storageOptions';
|
||||
|
||||
export class ElectronRendererSecureStorageService implements StorageService {
|
||||
async get<T>(key: string, options?: StorageServiceOptions): Promise<T> {
|
||||
async get<T>(key: string, options?: StorageOptions): Promise<T> {
|
||||
const val = ipcRenderer.sendSync('keytar', {
|
||||
action: 'getPassword',
|
||||
key: key,
|
||||
@@ -12,7 +14,7 @@ export class ElectronRendererSecureStorageService implements StorageService {
|
||||
return Promise.resolve(val != null ? JSON.parse(val) as T : null);
|
||||
}
|
||||
|
||||
async has(key: string, options?: StorageServiceOptions): Promise<boolean> {
|
||||
async has(key: string, options?: StorageOptions): Promise<boolean> {
|
||||
const val = ipcRenderer.sendSync('keytar', {
|
||||
action: 'hasPassword',
|
||||
key: key,
|
||||
@@ -21,7 +23,7 @@ export class ElectronRendererSecureStorageService implements StorageService {
|
||||
return Promise.resolve(!!val);
|
||||
}
|
||||
|
||||
async save(key: string, obj: any, options?: StorageServiceOptions): Promise<any> {
|
||||
async save(key: string, obj: any, options?: StorageOptions): Promise<any> {
|
||||
ipcRenderer.sendSync('keytar', {
|
||||
action: 'setPassword',
|
||||
key: key,
|
||||
@@ -31,7 +33,7 @@ export class ElectronRendererSecureStorageService implements StorageService {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async remove(key: string, options?: StorageServiceOptions): Promise<any> {
|
||||
async remove(key: string, options?: StorageOptions): Promise<any> {
|
||||
ipcRenderer.sendSync('keytar', {
|
||||
action: 'deletePassword',
|
||||
key: key,
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
app,
|
||||
BrowserWindow,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuItemConstructorOptions,
|
||||
nativeImage,
|
||||
Tray,
|
||||
@@ -10,9 +9,8 @@ import {
|
||||
import * as path from 'path';
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||
|
||||
import { ElectronConstants } from './electronConstants';
|
||||
import { WindowMain } from './window.main';
|
||||
|
||||
export class TrayMain {
|
||||
@@ -24,7 +22,7 @@ export class TrayMain {
|
||||
private pressedIcon: Electron.NativeImage;
|
||||
|
||||
constructor(private windowMain: WindowMain, private i18nService: I18nService,
|
||||
private storageService: StorageService) {
|
||||
private stateService: StateService) {
|
||||
if (process.platform === 'win32') {
|
||||
this.icon = path.join(__dirname, '/images/icon.ico');
|
||||
} else if (process.platform === 'darwin') {
|
||||
@@ -55,21 +53,21 @@ export class TrayMain {
|
||||
}
|
||||
|
||||
this.contextMenu = Menu.buildFromTemplate(menuItemOptions);
|
||||
if (await this.storageService.get<boolean>(ElectronConstants.enableTrayKey)) {
|
||||
if (await this.stateService.getEnableTray()) {
|
||||
this.showTray();
|
||||
}
|
||||
}
|
||||
|
||||
setupWindowListeners(win: BrowserWindow) {
|
||||
win.on('minimize', async (e: Event) => {
|
||||
if (await this.storageService.get<boolean>(ElectronConstants.enableMinimizeToTrayKey)) {
|
||||
if (await this.stateService.getEnableMinimizeToTray()) {
|
||||
e.preventDefault();
|
||||
this.hideToTray();
|
||||
}
|
||||
});
|
||||
|
||||
win.on('close', async (e: Event) => {
|
||||
if (await this.storageService.get<boolean>(ElectronConstants.enableCloseToTrayKey)) {
|
||||
if (await this.stateService.getEnableCloseToTray()) {
|
||||
if (!this.windowMain.isQuitting) {
|
||||
e.preventDefault();
|
||||
this.hideToTray();
|
||||
@@ -78,7 +76,7 @@ export class TrayMain {
|
||||
});
|
||||
|
||||
win.on('show', async (e: Event) => {
|
||||
const enableTray = await this.storageService.get<boolean>(ElectronConstants.enableTrayKey);
|
||||
const enableTray = await this.stateService.getEnableTray();
|
||||
if (!enableTray) {
|
||||
setTimeout(() => this.removeTray(false), 100);
|
||||
}
|
||||
@@ -103,7 +101,7 @@ export class TrayMain {
|
||||
if (this.windowMain.win != null) {
|
||||
this.windowMain.win.hide();
|
||||
}
|
||||
if (this.isDarwin() && !await this.storageService.get<boolean>(ElectronConstants.alwaysShowDock)) {
|
||||
if (this.isDarwin() && !await this.stateService.getAlwaysShowDock()) {
|
||||
this.hideDock();
|
||||
}
|
||||
}
|
||||
@@ -167,7 +165,7 @@ export class TrayMain {
|
||||
}
|
||||
if (this.windowMain.win.isVisible()) {
|
||||
this.windowMain.win.hide();
|
||||
if (this.isDarwin() && !await this.storageService.get<boolean>(ElectronConstants.alwaysShowDock)) {
|
||||
if (this.isDarwin() && !await this.stateService.getAlwaysShowDock()) {
|
||||
this.hideDock();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -25,8 +25,12 @@ export function isAppImage() {
|
||||
return process.platform === 'linux' && 'APPIMAGE' in process.env;
|
||||
}
|
||||
|
||||
export function isMac() {
|
||||
return process.platform === 'darwin';
|
||||
}
|
||||
|
||||
export function isMacAppStore() {
|
||||
return process.platform === 'darwin' && process.mas && process.mas === true;
|
||||
return isMac() && process.mas && process.mas === true;
|
||||
}
|
||||
|
||||
export function isWindowsStore() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user