1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 09:43:23 +00:00

Move to libs

This commit is contained in:
Hinton
2022-06-03 16:24:40 +02:00
parent 28d15bfe2a
commit d7492e3cf3
878 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
import { AuthenticationStatus } from "../../enums/authenticationStatus";
import { KdfType } from "../../enums/kdfType";
import { UriMatchType } from "../../enums/uriMatchType";
import { CipherData } from "../data/cipherData";
import { CollectionData } from "../data/collectionData";
import { EventData } from "../data/eventData";
import { FolderData } from "../data/folderData";
import { OrganizationData } from "../data/organizationData";
import { PolicyData } from "../data/policyData";
import { ProviderData } from "../data/providerData";
import { SendData } from "../data/sendData";
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 { EnvironmentUrls } from "./environmentUrls";
import { GeneratedPasswordHistory } from "./generatedPasswordHistory";
import { Policy } from "./policy";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
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;
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;
lastSync?: string;
userId?: string;
usesKeyConnector?: boolean;
keyHash?: string;
kdfIterations?: number;
kdfType?: KdfType;
}
export class AccountSettings {
autoConfirmFingerPrints?: boolean;
autoFillOnPageLoadDefault?: boolean;
biometricLocked?: boolean;
biometricUnlock?: boolean;
clearClipboard?: number;
collapsedGroupings?: string[];
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;
enableFullWidth?: boolean;
enableGravitars?: boolean;
environmentUrls: EnvironmentUrls = new EnvironmentUrls();
equivalentDomains?: any;
minimizeOnCopyToClipboard?: boolean;
neverDomains?: { [id: string]: any };
passwordGenerationOptions?: any;
usernameGenerationOptions?: any;
generatorOptions?: 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 = "lock";
}
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,87 @@
import { CryptoService } from "../../abstractions/crypto.service";
import { Utils } from "../../misc/utils";
import { AttachmentData } from "../data/attachmentData";
import { AttachmentView } from "../view/attachmentView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class Attachment extends Domain {
id: string;
url: string;
size: string;
sizeName: string; // Readable size, ex: "4.2 KB" or "1.43 GB"
key: EncString;
fileName: EncString;
constructor(obj?: AttachmentData) {
super();
if (obj == null) {
return;
}
this.size = obj.size;
this.buildDomainModel(
this,
obj,
{
id: null,
url: null,
sizeName: null,
fileName: null,
key: null,
},
["id", "url", "sizeName"]
);
}
async decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<AttachmentView> {
const view = await this.decryptObj(
new AttachmentView(this),
{
fileName: null,
},
orgId,
encKey
);
if (this.key != null) {
let cryptoService: CryptoService;
const containerService = (Utils.global as any).bitwardenContainerService;
if (containerService) {
cryptoService = containerService.getCryptoService();
} else {
throw new Error("global bitwardenContainerService not initialized.");
}
try {
const orgKey = await cryptoService.getOrgKey(orgId);
const decValue = await cryptoService.decryptToBytes(this.key, orgKey ?? encKey);
view.key = new SymmetricCryptoKey(decValue);
} catch (e) {
// TODO: error?
}
}
return view;
}
toAttachmentData(): AttachmentData {
const a = new AttachmentData();
a.size = this.size;
this.buildDataModel(
this,
a,
{
id: null,
url: null,
sizeName: null,
fileName: null,
key: null,
},
["id", "url", "sizeName"]
);
return a;
}
}

View File

@@ -0,0 +1,17 @@
import { TwoFactorProviderType } from "../../enums/twoFactorProviderType";
import { Utils } from "../../misc/utils";
export class AuthResult {
captchaSiteKey = "";
resetMasterPassword = false;
forcePasswordReset = false;
twoFactorProviders: Map<TwoFactorProviderType, { [key: string]: string }> = null;
get requiresCaptcha() {
return !Utils.isNullOrWhitespace(this.captchaSiteKey);
}
get requiresTwoFactor() {
return this.twoFactorProviders != null;
}
}

View File

@@ -0,0 +1,65 @@
import { CardData } from "../data/cardData";
import { CardView } from "../view/cardView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class Card extends Domain {
cardholderName: EncString;
brand: EncString;
number: EncString;
expMonth: EncString;
expYear: EncString;
code: EncString;
constructor(obj?: CardData) {
super();
if (obj == null) {
return;
}
this.buildDomainModel(
this,
obj,
{
cardholderName: null,
brand: null,
number: null,
expMonth: null,
expYear: null,
code: null,
},
[]
);
}
decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<CardView> {
return this.decryptObj(
new CardView(),
{
cardholderName: null,
brand: null,
number: null,
expMonth: null,
expYear: null,
code: null,
},
orgId,
encKey
);
}
toCardData(): CardData {
const c = new CardData();
this.buildDataModel(this, c, {
cardholderName: null,
brand: null,
number: null,
expMonth: null,
expYear: null,
code: null,
});
return c;
}
}

View File

@@ -0,0 +1,236 @@
import { CipherRepromptType } from "../../enums/cipherRepromptType";
import { CipherType } from "../../enums/cipherType";
import { CipherData } from "../data/cipherData";
import { CipherView } from "../view/cipherView";
import { Attachment } from "./attachment";
import { Card } from "./card";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { Field } from "./field";
import { Identity } from "./identity";
import { Login } from "./login";
import { Password } from "./password";
import { SecureNote } from "./secureNote";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class Cipher extends Domain {
id: string;
organizationId: string;
folderId: string;
name: EncString;
notes: EncString;
type: CipherType;
favorite: boolean;
organizationUseTotp: boolean;
edit: boolean;
viewPassword: boolean;
revisionDate: Date;
localData: any;
login: Login;
identity: Identity;
card: Card;
secureNote: SecureNote;
attachments: Attachment[];
fields: Field[];
passwordHistory: Password[];
collectionIds: string[];
deletedDate: Date;
reprompt: CipherRepromptType;
constructor(obj?: CipherData, localData: any = null) {
super();
if (obj == null) {
return;
}
this.buildDomainModel(
this,
obj,
{
id: null,
organizationId: null,
folderId: null,
name: null,
notes: null,
},
["id", "organizationId", "folderId"]
);
this.type = obj.type;
this.favorite = obj.favorite;
this.organizationUseTotp = obj.organizationUseTotp;
this.edit = obj.edit;
if (obj.viewPassword != null) {
this.viewPassword = obj.viewPassword;
} else {
this.viewPassword = true; // Default for already synced Ciphers without viewPassword
}
this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null;
this.collectionIds = obj.collectionIds;
this.localData = localData;
this.deletedDate = obj.deletedDate != null ? new Date(obj.deletedDate) : null;
this.reprompt = obj.reprompt;
switch (this.type) {
case CipherType.Login:
this.login = new Login(obj.login);
break;
case CipherType.SecureNote:
this.secureNote = new SecureNote(obj.secureNote);
break;
case CipherType.Card:
this.card = new Card(obj.card);
break;
case CipherType.Identity:
this.identity = new Identity(obj.identity);
break;
default:
break;
}
if (obj.attachments != null) {
this.attachments = obj.attachments.map((a) => new Attachment(a));
} else {
this.attachments = null;
}
if (obj.fields != null) {
this.fields = obj.fields.map((f) => new Field(f));
} else {
this.fields = null;
}
if (obj.passwordHistory != null) {
this.passwordHistory = obj.passwordHistory.map((ph) => new Password(ph));
} else {
this.passwordHistory = null;
}
}
async decrypt(encKey?: SymmetricCryptoKey): Promise<CipherView> {
const model = new CipherView(this);
await this.decryptObj(
model,
{
name: null,
notes: null,
},
this.organizationId,
encKey
);
switch (this.type) {
case CipherType.Login:
model.login = await this.login.decrypt(this.organizationId, encKey);
break;
case CipherType.SecureNote:
model.secureNote = await this.secureNote.decrypt(this.organizationId, encKey);
break;
case CipherType.Card:
model.card = await this.card.decrypt(this.organizationId, encKey);
break;
case CipherType.Identity:
model.identity = await this.identity.decrypt(this.organizationId, encKey);
break;
default:
break;
}
const orgId = this.organizationId;
if (this.attachments != null && this.attachments.length > 0) {
const attachments: any[] = [];
await this.attachments.reduce((promise, attachment) => {
return promise
.then(() => {
return attachment.decrypt(orgId, encKey);
})
.then((decAttachment) => {
attachments.push(decAttachment);
});
}, Promise.resolve());
model.attachments = attachments;
}
if (this.fields != null && this.fields.length > 0) {
const fields: any[] = [];
await this.fields.reduce((promise, field) => {
return promise
.then(() => {
return field.decrypt(orgId, encKey);
})
.then((decField) => {
fields.push(decField);
});
}, Promise.resolve());
model.fields = fields;
}
if (this.passwordHistory != null && this.passwordHistory.length > 0) {
const passwordHistory: any[] = [];
await this.passwordHistory.reduce((promise, ph) => {
return promise
.then(() => {
return ph.decrypt(orgId, encKey);
})
.then((decPh) => {
passwordHistory.push(decPh);
});
}, Promise.resolve());
model.passwordHistory = passwordHistory;
}
return model;
}
toCipherData(): CipherData {
const c = new CipherData();
c.id = this.id;
c.organizationId = this.organizationId;
c.folderId = this.folderId;
c.edit = this.edit;
c.viewPassword = this.viewPassword;
c.organizationUseTotp = this.organizationUseTotp;
c.favorite = this.favorite;
c.revisionDate = this.revisionDate != null ? this.revisionDate.toISOString() : null;
c.type = this.type;
c.collectionIds = this.collectionIds;
c.deletedDate = this.deletedDate != null ? this.deletedDate.toISOString() : null;
c.reprompt = this.reprompt;
this.buildDataModel(this, c, {
name: null,
notes: null,
});
switch (c.type) {
case CipherType.Login:
c.login = this.login.toLoginData();
break;
case CipherType.SecureNote:
c.secureNote = this.secureNote.toSecureNoteData();
break;
case CipherType.Card:
c.card = this.card.toCardData();
break;
case CipherType.Identity:
c.identity = this.identity.toIdentityData();
break;
default:
break;
}
if (this.fields != null) {
c.fields = this.fields.map((f) => f.toFieldData());
}
if (this.attachments != null) {
c.attachments = this.attachments.map((a) => a.toAttachmentData());
}
if (this.passwordHistory != null) {
c.passwordHistory = this.passwordHistory.map((ph) => ph.toPasswordHistoryData());
}
return c;
}
}

View File

@@ -0,0 +1,45 @@
import { CollectionData } from "../data/collectionData";
import { CollectionView } from "../view/collectionView";
import Domain from "./domainBase";
import { EncString } from "./encString";
export class Collection extends Domain {
id: string;
organizationId: string;
name: EncString;
externalId: string;
readOnly: boolean;
hidePasswords: boolean;
constructor(obj?: CollectionData) {
super();
if (obj == null) {
return;
}
this.buildDomainModel(
this,
obj,
{
id: null,
organizationId: null,
name: null,
externalId: null,
readOnly: null,
hidePasswords: null,
},
["id", "organizationId", "externalId", "readOnly", "hidePasswords"]
);
}
decrypt(): Promise<CollectionView> {
return this.decryptObj(
new CollectionView(this),
{
name: null,
},
this.organizationId
);
}
}

View File

@@ -0,0 +1,8 @@
export class DecryptParameters<T> {
encKey: T;
data: T;
iv: T;
macKey: T;
mac: T;
macData: T;
}

View File

@@ -0,0 +1,82 @@
import { View } from "../view/view";
import { EncString } from "./encString";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export default class Domain {
protected buildDomainModel<D extends Domain>(
domain: D,
dataObj: any,
map: any,
notEncList: any[] = []
) {
for (const prop in map) {
// eslint-disable-next-line
if (!map.hasOwnProperty(prop)) {
continue;
}
const objProp = dataObj[map[prop] || prop];
if (notEncList.indexOf(prop) > -1) {
(domain as any)[prop] = objProp ? objProp : null;
} else {
(domain as any)[prop] = objProp ? new EncString(objProp) : null;
}
}
}
protected buildDataModel<D extends Domain>(
domain: D,
dataObj: any,
map: any,
notEncStringList: any[] = []
) {
for (const prop in map) {
// eslint-disable-next-line
if (!map.hasOwnProperty(prop)) {
continue;
}
const objProp = (domain as any)[map[prop] || prop];
if (notEncStringList.indexOf(prop) > -1) {
(dataObj as any)[prop] = objProp != null ? objProp : null;
} else {
(dataObj as any)[prop] = objProp != null ? (objProp as EncString).encryptedString : null;
}
}
}
protected async decryptObj<T extends View>(
viewModel: T,
map: any,
orgId: string,
key: SymmetricCryptoKey = null
): Promise<T> {
const promises = [];
const self: any = this;
for (const prop in map) {
// eslint-disable-next-line
if (!map.hasOwnProperty(prop)) {
continue;
}
(function (theProp) {
const p = Promise.resolve()
.then(() => {
const mapProp = map[theProp] || theProp;
if (self[mapProp]) {
return self[mapProp].decrypt(orgId, key);
}
return null;
})
.then((val: any) => {
(viewModel as any)[theProp] = val;
});
promises.push(p);
})(prop);
}
await Promise.all(promises);
return viewModel;
}
}

View File

@@ -0,0 +1,3 @@
export class EncArrayBuffer {
constructor(public buffer: ArrayBuffer) {}
}

View File

@@ -0,0 +1,122 @@
import { CryptoService } from "../../abstractions/crypto.service";
import { EncryptionType } from "../../enums/encryptionType";
import { Utils } from "../../misc/utils";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class EncString {
encryptedString?: string;
encryptionType?: EncryptionType;
decryptedValue?: string;
data?: string;
iv?: string;
mac?: string;
constructor(
encryptedStringOrType: string | EncryptionType,
data?: string,
iv?: string,
mac?: string
) {
if (data != null) {
// data and header
const encType = encryptedStringOrType as EncryptionType;
if (iv != null) {
this.encryptedString = encType + "." + iv + "|" + data;
} else {
this.encryptedString = encType + "." + data;
}
// mac
if (mac != null) {
this.encryptedString += "|" + mac;
}
this.encryptionType = encType;
this.data = data;
this.iv = iv;
this.mac = mac;
return;
}
this.encryptedString = encryptedStringOrType as string;
if (!this.encryptedString) {
return;
}
const headerPieces = this.encryptedString.split(".");
let encPieces: string[] = null;
if (headerPieces.length === 2) {
try {
this.encryptionType = parseInt(headerPieces[0], null);
encPieces = headerPieces[1].split("|");
} catch (e) {
return;
}
} else {
encPieces = this.encryptedString.split("|");
this.encryptionType =
encPieces.length === 3
? EncryptionType.AesCbc128_HmacSha256_B64
: EncryptionType.AesCbc256_B64;
}
switch (this.encryptionType) {
case EncryptionType.AesCbc128_HmacSha256_B64:
case EncryptionType.AesCbc256_HmacSha256_B64:
if (encPieces.length !== 3) {
return;
}
this.iv = encPieces[0];
this.data = encPieces[1];
this.mac = encPieces[2];
break;
case EncryptionType.AesCbc256_B64:
if (encPieces.length !== 2) {
return;
}
this.iv = encPieces[0];
this.data = encPieces[1];
break;
case EncryptionType.Rsa2048_OaepSha256_B64:
case EncryptionType.Rsa2048_OaepSha1_B64:
if (encPieces.length !== 1) {
return;
}
this.data = encPieces[0];
break;
default:
return;
}
}
async decrypt(orgId: string, key: SymmetricCryptoKey = null): Promise<string> {
if (this.decryptedValue != null) {
return this.decryptedValue;
}
let cryptoService: CryptoService;
const containerService = (Utils.global as any).bitwardenContainerService;
if (containerService) {
cryptoService = containerService.getCryptoService();
} else {
throw new Error("global bitwardenContainerService not initialized.");
}
try {
if (key == null) {
key = await cryptoService.getOrgKey(orgId);
}
this.decryptedValue = await cryptoService.decryptToUtf8(this, key);
} catch (e) {
this.decryptedValue = "[error: cannot decrypt]";
}
return this.decryptedValue;
}
}

View File

@@ -0,0 +1,8 @@
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class EncryptedObject {
iv: ArrayBuffer;
data: ArrayBuffer;
mac: ArrayBuffer;
key: SymmetricCryptoKey;
}

View File

@@ -0,0 +1,10 @@
export class EnvironmentUrls {
base: string = null;
api: string = null;
identity: string = null;
icons: string = null;
notifications: string = null;
events: string = null;
webVault: string = null;
keyConnector: string = null;
}

View File

@@ -0,0 +1,62 @@
import { FieldType } from "../../enums/fieldType";
import { LinkedIdType } from "../../enums/linkedIdType";
import { FieldData } from "../data/fieldData";
import { FieldView } from "../view/fieldView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class Field extends Domain {
name: EncString;
value: EncString;
type: FieldType;
linkedId: LinkedIdType;
constructor(obj?: FieldData) {
super();
if (obj == null) {
return;
}
this.type = obj.type;
this.linkedId = obj.linkedId;
this.buildDomainModel(
this,
obj,
{
name: null,
value: null,
},
[]
);
}
decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<FieldView> {
return this.decryptObj(
new FieldView(this),
{
name: null,
value: null,
},
orgId,
encKey
);
}
toFieldData(): FieldData {
const f = new FieldData();
this.buildDataModel(
this,
f,
{
name: null,
value: null,
type: null,
linkedId: null,
},
["type", "linkedId"]
);
return f;
}
}

View File

@@ -0,0 +1,40 @@
import { FolderData } from "../data/folderData";
import { FolderView } from "../view/folderView";
import Domain from "./domainBase";
import { EncString } from "./encString";
export class Folder extends Domain {
id: string;
name: EncString;
revisionDate: Date;
constructor(obj?: FolderData) {
super();
if (obj == null) {
return;
}
this.buildDomainModel(
this,
obj,
{
id: null,
name: null,
},
["id"]
);
this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null;
}
decrypt(): Promise<FolderView> {
return this.decryptObj(
new FolderView(this),
{
name: null,
},
null
);
}
}

View File

@@ -0,0 +1,9 @@
export class GeneratedPasswordHistory {
password: string;
date: number;
constructor(password: string, date: number) {
this.password = password;
this.date = date;
}
}

View File

@@ -0,0 +1,40 @@
import { StateVersion } from "../../enums/stateVersion";
import { ThemeType } from "../../enums/themeType";
import { EnvironmentUrls } from "./environmentUrls";
import { WindowState } from "./windowState";
export class GlobalState {
enableAlwaysOnTop?: boolean;
installedVersion?: string;
locale?: string;
organizationInvitation?: any;
ssoCodeVerifier?: string;
ssoOrganizationIdentifier?: string;
ssoState?: string;
rememberedEmail?: string;
theme?: ThemeType = ThemeType.System;
window?: WindowState = new WindowState();
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: StateVersion = StateVersion.One;
environmentUrls: EnvironmentUrls = new EnvironmentUrls();
enableTray?: boolean;
enableMinimizeToTray?: boolean;
enableCloseToTray?: boolean;
enableStartToTray?: boolean;
openAtLogin?: boolean;
alwaysShowDock?: boolean;
enableBrowserIntegration?: boolean;
enableBrowserIntegrationFingerprint?: boolean;
}

View File

@@ -0,0 +1,113 @@
import { IdentityData } from "../data/identityData";
import { IdentityView } from "../view/identityView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class Identity extends Domain {
title: EncString;
firstName: EncString;
middleName: EncString;
lastName: EncString;
address1: EncString;
address2: EncString;
address3: EncString;
city: EncString;
state: EncString;
postalCode: EncString;
country: EncString;
company: EncString;
email: EncString;
phone: EncString;
ssn: EncString;
username: EncString;
passportNumber: EncString;
licenseNumber: EncString;
constructor(obj?: IdentityData) {
super();
if (obj == null) {
return;
}
this.buildDomainModel(
this,
obj,
{
title: null,
firstName: null,
middleName: null,
lastName: null,
address1: null,
address2: null,
address3: null,
city: null,
state: null,
postalCode: null,
country: null,
company: null,
email: null,
phone: null,
ssn: null,
username: null,
passportNumber: null,
licenseNumber: null,
},
[]
);
}
decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<IdentityView> {
return this.decryptObj(
new IdentityView(),
{
title: null,
firstName: null,
middleName: null,
lastName: null,
address1: null,
address2: null,
address3: null,
city: null,
state: null,
postalCode: null,
country: null,
company: null,
email: null,
phone: null,
ssn: null,
username: null,
passportNumber: null,
licenseNumber: null,
},
orgId,
encKey
);
}
toIdentityData(): IdentityData {
const i = new IdentityData();
this.buildDataModel(this, i, {
title: null,
firstName: null,
middleName: null,
lastName: null,
address1: null,
address2: null,
address3: null,
city: null,
state: null,
postalCode: null,
country: null,
company: null,
email: null,
phone: null,
ssn: null,
username: null,
passportNumber: null,
licenseNumber: null,
});
return i;
}
}

View File

@@ -0,0 +1,14 @@
import { CipherView } from "../view/cipherView";
import { CollectionView } from "../view/collectionView";
import { FolderView } from "../view/folderView";
export class ImportResult {
success = false;
missingPassword = false;
errorMessage: string;
ciphers: CipherView[] = [];
folders: FolderView[] = [];
folderRelationships: [number, number][] = [];
collections: CollectionView[] = [];
collectionRelationships: [number, number][] = [];
}

View File

@@ -0,0 +1,31 @@
import { AuthenticationType } from "../../enums/authenticationType";
import { TokenRequestTwoFactor } from "../request/identityToken/tokenRequestTwoFactor";
export class PasswordLogInCredentials {
readonly type = AuthenticationType.Password;
constructor(
public email: string,
public masterPassword: string,
public captchaToken?: string,
public twoFactor?: TokenRequestTwoFactor
) {}
}
export class SsoLogInCredentials {
readonly type = AuthenticationType.Sso;
constructor(
public code: string,
public codeVerifier: string,
public redirectUrl: string,
public orgId: string,
public twoFactor?: TokenRequestTwoFactor
) {}
}
export class ApiLogInCredentials {
readonly type = AuthenticationType.Api;
constructor(public clientId: string, public clientSecret: string) {}
}

View File

@@ -0,0 +1,88 @@
import { LoginData } from "../data/loginData";
import { LoginView } from "../view/loginView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { LoginUri } from "./loginUri";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class Login extends Domain {
uris: LoginUri[];
username: EncString;
password: EncString;
passwordRevisionDate?: Date;
totp: EncString;
autofillOnPageLoad: boolean;
constructor(obj?: LoginData) {
super();
if (obj == null) {
return;
}
this.passwordRevisionDate =
obj.passwordRevisionDate != null ? new Date(obj.passwordRevisionDate) : null;
this.autofillOnPageLoad = obj.autofillOnPageLoad;
this.buildDomainModel(
this,
obj,
{
username: null,
password: null,
totp: null,
},
[]
);
if (obj.uris) {
this.uris = [];
obj.uris.forEach((u) => {
this.uris.push(new LoginUri(u));
});
}
}
async decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<LoginView> {
const view = await this.decryptObj(
new LoginView(this),
{
username: null,
password: null,
totp: null,
},
orgId,
encKey
);
if (this.uris != null) {
view.uris = [];
for (let i = 0; i < this.uris.length; i++) {
const uri = await this.uris[i].decrypt(orgId, encKey);
view.uris.push(uri);
}
}
return view;
}
toLoginData(): LoginData {
const l = new LoginData();
l.passwordRevisionDate =
this.passwordRevisionDate != null ? this.passwordRevisionDate.toISOString() : null;
l.autofillOnPageLoad = this.autofillOnPageLoad;
this.buildDataModel(this, l, {
username: null,
password: null,
totp: null,
});
if (this.uris != null && this.uris.length > 0) {
l.uris = [];
this.uris.forEach((u) => {
l.uris.push(u.toLoginUriData());
});
}
return l;
}
}

View File

@@ -0,0 +1,54 @@
import { UriMatchType } from "../../enums/uriMatchType";
import { LoginUriData } from "../data/loginUriData";
import { LoginUriView } from "../view/loginUriView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class LoginUri extends Domain {
uri: EncString;
match: UriMatchType;
constructor(obj?: LoginUriData) {
super();
if (obj == null) {
return;
}
this.match = obj.match;
this.buildDomainModel(
this,
obj,
{
uri: null,
},
[]
);
}
decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<LoginUriView> {
return this.decryptObj(
new LoginUriView(this),
{
uri: null,
},
orgId,
encKey
);
}
toLoginUriData(): LoginUriData {
const u = new LoginUriData();
this.buildDataModel(
this,
u,
{
uri: null,
match: null,
},
["match"]
);
return u;
}
}

View File

@@ -0,0 +1,10 @@
import Domain from "./domainBase";
export class MasterPasswordPolicyOptions extends Domain {
minComplexity = 0;
minLength = 0;
requireUpper = false;
requireLower = false;
requireNumbers = false;
requireSpecial = false;
}

View File

@@ -0,0 +1,222 @@
import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType";
import { OrganizationUserType } from "../../enums/organizationUserType";
import { Permissions } from "../../enums/permissions";
import { ProductType } from "../../enums/productType";
import { PermissionsApi } from "../api/permissionsApi";
import { OrganizationData } from "../data/organizationData";
export class Organization {
id: string;
name: string;
status: OrganizationUserStatusType;
type: OrganizationUserType;
enabled: boolean;
usePolicies: boolean;
useGroups: boolean;
useDirectory: boolean;
useEvents: boolean;
useTotp: boolean;
use2fa: boolean;
useApi: boolean;
useSso: boolean;
useKeyConnector: boolean;
useResetPassword: boolean;
selfHost: boolean;
usersGetPremium: boolean;
seats: number;
maxCollections: number;
maxStorageGb?: number;
ssoBound: boolean;
identifier: string;
permissions: PermissionsApi;
resetPasswordEnrolled: boolean;
userId: string;
hasPublicAndPrivateKeys: boolean;
providerId: string;
providerName: string;
isProviderUser: boolean;
familySponsorshipFriendlyName: string;
familySponsorshipAvailable: boolean;
planProductType: ProductType;
keyConnectorEnabled: boolean;
keyConnectorUrl: string;
familySponsorshipLastSyncDate?: Date;
familySponsorshipValidUntil?: Date;
familySponsorshipToDelete?: boolean;
constructor(obj?: OrganizationData) {
if (obj == null) {
return;
}
this.id = obj.id;
this.name = obj.name;
this.status = obj.status;
this.type = obj.type;
this.enabled = obj.enabled;
this.usePolicies = obj.usePolicies;
this.useGroups = obj.useGroups;
this.useDirectory = obj.useDirectory;
this.useEvents = obj.useEvents;
this.useTotp = obj.useTotp;
this.use2fa = obj.use2fa;
this.useApi = obj.useApi;
this.useSso = obj.useSso;
this.useKeyConnector = obj.useKeyConnector;
this.useResetPassword = obj.useResetPassword;
this.selfHost = obj.selfHost;
this.usersGetPremium = obj.usersGetPremium;
this.seats = obj.seats;
this.maxCollections = obj.maxCollections;
this.maxStorageGb = obj.maxStorageGb;
this.ssoBound = obj.ssoBound;
this.identifier = obj.identifier;
this.permissions = obj.permissions;
this.resetPasswordEnrolled = obj.resetPasswordEnrolled;
this.userId = obj.userId;
this.hasPublicAndPrivateKeys = obj.hasPublicAndPrivateKeys;
this.providerId = obj.providerId;
this.providerName = obj.providerName;
this.isProviderUser = obj.isProviderUser;
this.familySponsorshipFriendlyName = obj.familySponsorshipFriendlyName;
this.familySponsorshipAvailable = obj.familySponsorshipAvailable;
this.planProductType = obj.planProductType;
this.keyConnectorEnabled = obj.keyConnectorEnabled;
this.keyConnectorUrl = obj.keyConnectorUrl;
this.familySponsorshipLastSyncDate = obj.familySponsorshipLastSyncDate;
this.familySponsorshipValidUntil = obj.familySponsorshipValidUntil;
this.familySponsorshipToDelete = obj.familySponsorshipToDelete;
}
get canAccess() {
if (this.type === OrganizationUserType.Owner) {
return true;
}
return this.enabled && this.status === OrganizationUserStatusType.Confirmed;
}
get isManager() {
return (
this.type === OrganizationUserType.Manager ||
this.type === OrganizationUserType.Owner ||
this.type === OrganizationUserType.Admin
);
}
get isAdmin() {
return this.type === OrganizationUserType.Owner || this.type === OrganizationUserType.Admin;
}
get isOwner() {
return this.type === OrganizationUserType.Owner || this.isProviderUser;
}
get canAccessEventLogs() {
return this.isAdmin || this.permissions.accessEventLogs;
}
get canAccessImportExport() {
return this.isAdmin || this.permissions.accessImportExport;
}
get canAccessReports() {
return this.isAdmin || this.permissions.accessReports;
}
get canCreateNewCollections() {
return (
this.isManager ||
(this.permissions.createNewCollections ?? this.permissions.manageAllCollections)
);
}
get canEditAnyCollection() {
return (
this.isAdmin || (this.permissions.editAnyCollection ?? this.permissions.manageAllCollections)
);
}
get canDeleteAnyCollection() {
return (
this.isAdmin ||
(this.permissions.deleteAnyCollection ?? this.permissions.manageAllCollections)
);
}
get canViewAllCollections() {
return this.canCreateNewCollections || this.canEditAnyCollection || this.canDeleteAnyCollection;
}
get canEditAssignedCollections() {
return (
this.isManager ||
(this.permissions.editAssignedCollections ?? this.permissions.manageAssignedCollections)
);
}
get canDeleteAssignedCollections() {
return (
this.isManager ||
(this.permissions.deleteAssignedCollections ?? this.permissions.manageAssignedCollections)
);
}
get canViewAssignedCollections() {
return this.canDeleteAssignedCollections || this.canEditAssignedCollections;
}
get canManageGroups() {
return this.isAdmin || this.permissions.manageGroups;
}
get canManageSso() {
return this.isAdmin || this.permissions.manageSso;
}
get canManagePolicies() {
return this.isAdmin || this.permissions.managePolicies;
}
get canManageUsers() {
return this.isAdmin || this.permissions.manageUsers;
}
get canManageUsersPassword() {
return this.isAdmin || this.permissions.manageResetPassword;
}
get isExemptFromPolicies() {
return this.canManagePolicies;
}
hasAnyPermission(permissions: Permissions[]) {
const specifiedPermissions =
(permissions.includes(Permissions.AccessEventLogs) && this.canAccessEventLogs) ||
(permissions.includes(Permissions.AccessImportExport) && this.canAccessImportExport) ||
(permissions.includes(Permissions.AccessReports) && this.canAccessReports) ||
(permissions.includes(Permissions.CreateNewCollections) && this.canCreateNewCollections) ||
(permissions.includes(Permissions.EditAnyCollection) && this.canEditAnyCollection) ||
(permissions.includes(Permissions.DeleteAnyCollection) && this.canDeleteAnyCollection) ||
(permissions.includes(Permissions.EditAssignedCollections) &&
this.canEditAssignedCollections) ||
(permissions.includes(Permissions.DeleteAssignedCollections) &&
this.canDeleteAssignedCollections) ||
(permissions.includes(Permissions.ManageGroups) && this.canManageGroups) ||
(permissions.includes(Permissions.ManageOrganization) && this.isOwner) ||
(permissions.includes(Permissions.ManagePolicies) && this.canManagePolicies) ||
(permissions.includes(Permissions.ManageUsers) && this.canManageUsers) ||
(permissions.includes(Permissions.ManageUsersPassword) && this.canManageUsersPassword) ||
(permissions.includes(Permissions.ManageSso) && this.canManageSso) ||
(permissions.includes(Permissions.ManageBilling) && this.canManageBilling);
return specifiedPermissions && (this.enabled || this.isOwner);
}
get canManageBilling() {
return this.isOwner && (this.isProviderUser || !this.hasProvider);
}
get hasProvider() {
return this.providerId != null || this.providerName != null;
}
}

View File

@@ -0,0 +1,43 @@
import { PasswordHistoryData } from "../data/passwordHistoryData";
import { PasswordHistoryView } from "../view/passwordHistoryView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class Password extends Domain {
password: EncString;
lastUsedDate: Date;
constructor(obj?: PasswordHistoryData) {
super();
if (obj == null) {
return;
}
this.buildDomainModel(this, obj, {
password: null,
});
this.lastUsedDate = new Date(obj.lastUsedDate);
}
decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<PasswordHistoryView> {
return this.decryptObj(
new PasswordHistoryView(this),
{
password: null,
},
orgId,
encKey
);
}
toPasswordHistoryData(): PasswordHistoryData {
const ph = new PasswordHistoryData();
ph.lastUsedDate = this.lastUsedDate.toISOString();
this.buildDataModel(this, ph, {
password: null,
});
return ph;
}
}

View File

@@ -0,0 +1,31 @@
import Domain from "./domainBase";
export class PasswordGeneratorPolicyOptions extends Domain {
defaultType = "";
minLength = 0;
useUppercase = false;
useLowercase = false;
useNumbers = false;
numberCount = 0;
useSpecial = false;
specialCount = 0;
minNumberWords = 0;
capitalize = false;
includeNumber = false;
inEffect() {
return (
this.defaultType !== "" ||
this.minLength > 0 ||
this.numberCount > 0 ||
this.specialCount > 0 ||
this.useUppercase ||
this.useLowercase ||
this.useNumbers ||
this.useSpecial ||
this.minNumberWords > 0 ||
this.capitalize ||
this.includeNumber
);
}
}

View File

@@ -0,0 +1,25 @@
import { PolicyType } from "../../enums/policyType";
import { PolicyData } from "../data/policyData";
import Domain from "./domainBase";
export class Policy extends Domain {
id: string;
organizationId: string;
type: PolicyType;
data: any;
enabled: boolean;
constructor(obj?: PolicyData) {
super();
if (obj == null) {
return;
}
this.id = obj.id;
this.organizationId = obj.organizationId;
this.type = obj.type;
this.data = obj.data;
this.enabled = obj.enabled;
}
}

View File

@@ -0,0 +1,50 @@
import { ProviderUserStatusType } from "../../enums/providerUserStatusType";
import { ProviderUserType } from "../../enums/providerUserType";
import { ProviderData } from "../data/providerData";
export class Provider {
id: string;
name: string;
status: ProviderUserStatusType;
type: ProviderUserType;
enabled: boolean;
userId: string;
useEvents: boolean;
constructor(obj?: ProviderData) {
if (obj == null) {
return;
}
this.id = obj.id;
this.name = obj.name;
this.status = obj.status;
this.type = obj.type;
this.enabled = obj.enabled;
this.userId = obj.userId;
this.useEvents = obj.useEvents;
}
get canAccess() {
if (this.isProviderAdmin) {
return true;
}
return this.enabled && this.status === ProviderUserStatusType.Confirmed;
}
get canCreateOrganizations() {
return this.enabled && this.isProviderAdmin;
}
get canManageUsers() {
return this.isProviderAdmin;
}
get canAccessEventLogs() {
return this.isProviderAdmin;
}
get isProviderAdmin() {
return this.type === ProviderUserType.ProviderAdmin;
}
}

View File

@@ -0,0 +1,5 @@
import Domain from "./domainBase";
export class ResetPasswordPolicyOptions extends Domain {
autoEnrollEnabled = false;
}

View File

@@ -0,0 +1,29 @@
import { SecureNoteType } from "../../enums/secureNoteType";
import { SecureNoteData } from "../data/secureNoteData";
import { SecureNoteView } from "../view/secureNoteView";
import Domain from "./domainBase";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class SecureNote extends Domain {
type: SecureNoteType;
constructor(obj?: SecureNoteData) {
super();
if (obj == null) {
return;
}
this.type = obj.type;
}
decrypt(orgId: string, encKey?: SymmetricCryptoKey): Promise<SecureNoteView> {
return Promise.resolve(new SecureNoteView(this));
}
toSecureNoteData(): SecureNoteData {
const n = new SecureNoteData();
n.type = this.type;
return n;
}
}

View File

@@ -0,0 +1,112 @@
import { CryptoService } from "../../abstractions/crypto.service";
import { SendType } from "../../enums/sendType";
import { Utils } from "../../misc/utils";
import { SendData } from "../data/sendData";
import { SendView } from "../view/sendView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SendFile } from "./sendFile";
import { SendText } from "./sendText";
export class Send extends Domain {
id: string;
accessId: string;
type: SendType;
name: EncString;
notes: EncString;
file: SendFile;
text: SendText;
key: EncString;
maxAccessCount?: number;
accessCount: number;
revisionDate: Date;
expirationDate: Date;
deletionDate: Date;
password: string;
disabled: boolean;
hideEmail: boolean;
constructor(obj?: SendData) {
super();
if (obj == null) {
return;
}
this.buildDomainModel(
this,
obj,
{
id: null,
accessId: null,
name: null,
notes: null,
key: null,
},
["id", "accessId"]
);
this.type = obj.type;
this.maxAccessCount = obj.maxAccessCount;
this.accessCount = obj.accessCount;
this.password = obj.password;
this.disabled = obj.disabled;
this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null;
this.deletionDate = obj.deletionDate != null ? new Date(obj.deletionDate) : null;
this.expirationDate = obj.expirationDate != null ? new Date(obj.expirationDate) : null;
this.hideEmail = obj.hideEmail;
switch (this.type) {
case SendType.Text:
this.text = new SendText(obj.text);
break;
case SendType.File:
this.file = new SendFile(obj.file);
break;
default:
break;
}
}
async decrypt(): Promise<SendView> {
const model = new SendView(this);
let cryptoService: CryptoService;
const containerService = (Utils.global as any).bitwardenContainerService;
if (containerService) {
cryptoService = containerService.getCryptoService();
} else {
throw new Error("global bitwardenContainerService not initialized.");
}
try {
model.key = await cryptoService.decryptToBytes(this.key, null);
model.cryptoKey = await cryptoService.makeSendKey(model.key);
} catch (e) {
// TODO: error?
}
await this.decryptObj(
model,
{
name: null,
notes: null,
},
null,
model.cryptoKey
);
switch (this.type) {
case SendType.File:
model.file = await this.file.decrypt(model.cryptoKey);
break;
case SendType.Text:
model.text = await this.text.decrypt(model.cryptoKey);
break;
default:
break;
}
return model;
}
}

View File

@@ -0,0 +1,77 @@
import { SendType } from "../../enums/sendType";
import { SendAccessResponse } from "../response/sendAccessResponse";
import { SendAccessView } from "../view/sendAccessView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SendFile } from "./sendFile";
import { SendText } from "./sendText";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class SendAccess extends Domain {
id: string;
type: SendType;
name: EncString;
file: SendFile;
text: SendText;
expirationDate: Date;
creatorIdentifier: string;
constructor(obj?: SendAccessResponse) {
super();
if (obj == null) {
return;
}
this.buildDomainModel(
this,
obj,
{
id: null,
name: null,
expirationDate: null,
creatorIdentifier: null,
},
["id", "expirationDate", "creatorIdentifier"]
);
this.type = obj.type;
switch (this.type) {
case SendType.Text:
this.text = new SendText(obj.text);
break;
case SendType.File:
this.file = new SendFile(obj.file);
break;
default:
break;
}
}
async decrypt(key: SymmetricCryptoKey): Promise<SendAccessView> {
const model = new SendAccessView(this);
await this.decryptObj(
model,
{
name: null,
},
null,
key
);
switch (this.type) {
case SendType.File:
model.file = await this.file.decrypt(key);
break;
case SendType.Text:
model.text = await this.text.decrypt(key);
break;
default:
break;
}
return model;
}
}

View File

@@ -0,0 +1,44 @@
import { SendFileData } from "../data/sendFileData";
import { SendFileView } from "../view/sendFileView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class SendFile extends Domain {
id: string;
size: string;
sizeName: string;
fileName: EncString;
constructor(obj?: SendFileData) {
super();
if (obj == null) {
return;
}
this.size = obj.size;
this.buildDomainModel(
this,
obj,
{
id: null,
sizeName: null,
fileName: null,
},
["id", "sizeName"]
);
}
async decrypt(key: SymmetricCryptoKey): Promise<SendFileView> {
const view = await this.decryptObj(
new SendFileView(this),
{
fileName: null,
},
null,
key
);
return view;
}
}

View File

@@ -0,0 +1,39 @@
import { SendTextData } from "../data/sendTextData";
import { SendTextView } from "../view/sendTextView";
import Domain from "./domainBase";
import { EncString } from "./encString";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class SendText extends Domain {
text: EncString;
hidden: boolean;
constructor(obj?: SendTextData) {
super();
if (obj == null) {
return;
}
this.hidden = obj.hidden;
this.buildDomainModel(
this,
obj,
{
text: null,
},
[]
);
}
decrypt(key: SymmetricCryptoKey): Promise<SendTextView> {
return this.decryptObj(
new SendTextView(this),
{
text: null,
},
null,
key
);
}
}

View File

@@ -0,0 +1,87 @@
import { CipherView } from "../view/cipherView";
const CacheTTL = 3000;
export class SortedCiphersCache {
private readonly sortedCiphersByUrl: Map<string, Ciphers> = new Map<string, Ciphers>();
private readonly timeouts: Map<string, any> = new Map<string, any>();
constructor(private readonly comparator: (a: CipherView, b: CipherView) => number) {}
isCached(url: string) {
return this.sortedCiphersByUrl.has(url);
}
addCiphers(url: string, ciphers: CipherView[]) {
ciphers.sort(this.comparator);
this.sortedCiphersByUrl.set(url, new Ciphers(ciphers));
this.resetTimer(url);
}
getLastUsed(url: string) {
this.resetTimer(url);
return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getLastUsed() : null;
}
getLastLaunched(url: string) {
return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getLastLaunched() : null;
}
getNext(url: string) {
this.resetTimer(url);
return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getNext() : null;
}
updateLastUsedIndex(url: string) {
if (this.isCached(url)) {
this.sortedCiphersByUrl.get(url).updateLastUsedIndex();
}
}
clear() {
this.sortedCiphersByUrl.clear();
this.timeouts.clear();
}
private resetTimer(url: string) {
clearTimeout(this.timeouts.get(url));
this.timeouts.set(
url,
setTimeout(() => {
this.sortedCiphersByUrl.delete(url);
this.timeouts.delete(url);
}, CacheTTL)
);
}
}
class Ciphers {
lastUsedIndex = -1;
constructor(private readonly ciphers: CipherView[]) {}
getLastUsed() {
this.lastUsedIndex = Math.max(this.lastUsedIndex, 0);
return this.ciphers[this.lastUsedIndex];
}
getLastLaunched() {
const usedCiphers = this.ciphers.filter((cipher) => cipher.localData?.lastLaunched);
const sortedCiphers = usedCiphers.sort(
(x, y) => y.localData.lastLaunched.valueOf() - x.localData.lastLaunched.valueOf()
);
return sortedCiphers[0];
}
getNextIndex() {
return (this.lastUsedIndex + 1) % this.ciphers.length;
}
getNext() {
return this.ciphers[this.getNextIndex()];
}
updateLastUsedIndex() {
this.lastUsedIndex = this.getNextIndex();
}
}

View File

@@ -0,0 +1,17 @@
import { Account } from "./account";
import { GlobalState } from "./globalState";
export class State<
TGlobalState extends GlobalState = GlobalState,
TAccount extends Account = Account
> {
accounts: { [userId: string]: TAccount } = {};
globals: TGlobalState;
activeUserId: string;
authenticatedAccounts: string[] = [];
accountActivity: { [userId: string]: number } = {};
constructor(globals: TGlobalState) {
this.globals = globals;
}
}

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

@@ -0,0 +1,57 @@
import { EncryptionType } from "../../enums/encryptionType";
import { Utils } from "../../misc/utils";
export class SymmetricCryptoKey {
key: ArrayBuffer;
encKey?: ArrayBuffer;
macKey?: ArrayBuffer;
encType: EncryptionType;
keyB64: string;
encKeyB64: string;
macKeyB64: string;
meta: any;
constructor(key: ArrayBuffer, encType?: EncryptionType) {
if (key == null) {
throw new Error("Must provide key");
}
if (encType == null) {
if (key.byteLength === 32) {
encType = EncryptionType.AesCbc256_B64;
} else if (key.byteLength === 64) {
encType = EncryptionType.AesCbc256_HmacSha256_B64;
} else {
throw new Error("Unable to determine encType.");
}
}
this.key = key;
this.encType = encType;
if (encType === EncryptionType.AesCbc256_B64 && key.byteLength === 32) {
this.encKey = key;
this.macKey = null;
} else if (encType === EncryptionType.AesCbc128_HmacSha256_B64 && key.byteLength === 32) {
this.encKey = key.slice(0, 16);
this.macKey = key.slice(16, 32);
} else if (encType === EncryptionType.AesCbc256_HmacSha256_B64 && key.byteLength === 64) {
this.encKey = key.slice(0, 32);
this.macKey = key.slice(32, 64);
} else {
throw new Error("Unsupported encType/key length.");
}
if (this.key != null) {
this.keyB64 = Utils.fromBufferToB64(this.key);
}
if (this.encKey != null) {
this.encKeyB64 = Utils.fromBufferToB64(this.encKey);
}
if (this.macKey != null) {
this.macKeyB64 = Utils.fromBufferToB64(this.macKey);
}
}
}

View File

@@ -0,0 +1,16 @@
export class TreeNode<T extends ITreeNodeObject> {
parent: T;
node: T;
children: TreeNode<T>[] = [];
constructor(node: T, name: string, parent: T) {
this.parent = parent;
this.node = node;
this.node.name = name;
}
}
export interface ITreeNodeObject {
id: string;
name: string;
}

View File

@@ -0,0 +1,10 @@
export class WindowState {
width?: number;
height?: number;
isMaximized?: boolean;
// TODO: displayBounds is an Electron.Rectangle.
// We need to establish some kind of client-specific global state, similiar to the way we already extend a base Account.
displayBounds: any;
x?: number;
y?: number;
}