1
0
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:
Hinton
2022-01-03 12:46:02 +01:00
108 changed files with 11516 additions and 8636 deletions

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1 @@
_

4
.husky/pre-commit Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
dist

3
.prettierrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"printWidth": 100
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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);

View File

@@ -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 = '') {

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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)) {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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": {

View File

@@ -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",

View 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');
});
});

View File

@@ -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));

View File

@@ -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>;
}

View File

@@ -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>;

View File

@@ -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>;

View File

@@ -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,

View File

@@ -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>;
}

View File

@@ -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[]>;

View 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>;
}

View File

@@ -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;
}

View File

@@ -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>;
}

View 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>;
}

View File

@@ -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[]>;

View File

@@ -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>;
}

View File

@@ -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>;
}

View File

@@ -0,0 +1,4 @@
export abstract class StateMigrationService {
needsMigration: () => Promise<boolean>;
migrate: () => Promise<void>;
}

View File

@@ -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';

View File

@@ -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>;

View File

@@ -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>;
}

View File

@@ -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>;
}

View File

@@ -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>;
}

View File

@@ -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>;
}

View File

@@ -0,0 +1,6 @@
export enum AuthenticationStatus {
Locked = 'locked',
Unlocked = 'unlocked',
LoggedOut = 'loggedOut',
Active = 'active',
}

View File

@@ -0,0 +1,5 @@
export enum HtmlStorageLocation {
Local = 'local',
Memory = 'memory',
Session = 'session',
}

View File

@@ -0,0 +1,4 @@
export enum KeySuffixOptions {
Auto = 'auto',
Biometric = 'biometric',
}

View File

@@ -0,0 +1,5 @@
export enum StorageLocation {
Both = 'both',
Disk = 'disk',
Memory = 'memory',
}

View File

@@ -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);

View 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,
},
});
}
}

View 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;
}

View 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;
}

View 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,
};

View File

@@ -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',

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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 });
}
}

View File

@@ -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,

View File

@@ -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 });
}
}

View File

@@ -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) {

View File

@@ -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() {

View File

@@ -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;
}

View 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);
}
}

View File

@@ -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 {

View File

@@ -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) {

View 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);
}
}

View File

@@ -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.');
};
});

View 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

View 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);
}
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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';
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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"

View File

@@ -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"
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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';
}

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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