1
0
mirror of https://github.com/bitwarden/directory-connector synced 2026-01-06 02:23:35 +00:00
Files
directory-connector/src/services/stateMigration.service.ts
2022-03-03 11:09:04 +01:00

159 lines
6.3 KiB
TypeScript

import { StateVersion } from "jslib-common/enums/stateVersion";
import { StateMigrationService as BaseStateMigrationService } from "jslib-common/services/stateMigration.service";
import { DirectoryType } from "src/enums/directoryType";
import { Account, DirectoryConfigurations, DirectorySettings } from "src/models/account";
import { AzureConfiguration } from "src/models/azureConfiguration";
import { GSuiteConfiguration } from "src/models/gsuiteConfiguration";
import { LdapConfiguration } from "src/models/ldapConfiguration";
import { OktaConfiguration } from "src/models/oktaConfiguration";
import { OneLoginConfiguration } from "src/models/oneLoginConfiguration";
import { SyncConfiguration } from "src/models/syncConfiguration";
const SecureStorageKeys: { [key: string]: any } = {
ldap: "ldapPassword",
gsuite: "gsuitePrivateKey",
azure: "azureKey",
okta: "oktaToken",
oneLogin: "oneLoginClientSecret",
directoryConfigPrefix: "directoryConfig_",
sync: "syncConfig",
directoryType: "directoryType",
userDelta: "userDeltaToken",
groupDelta: "groupDeltaToken",
organizationId: "organizationId",
};
const Keys: { [key: string]: any } = {
entityId: "entityId",
directoryType: "directoryType",
organizationId: "organizationId",
lastUserSync: "lastUserSync",
lastGroupSync: "lastGroupSync",
lastSyncHash: "lastSyncHash",
syncingDir: "syncingDir",
syncConfig: "syncConfig",
tempDirectoryConfigs: "tempDirectoryConfigs",
tempDirectorySettings: "tempDirectorySettings",
};
const ClientKeys: { [key: string]: any } = {
clientIdOld: "clientId",
clientId: "apikey_clientId",
clientSecretOld: "clientSecret",
clientSecret: "apikey_clientSecret",
};
export class StateMigrationService extends BaseStateMigrationService {
async migrate(): Promise<void> {
let currentStateVersion = await this.getCurrentStateVersion();
while (currentStateVersion < StateVersion.Latest) {
switch (currentStateVersion) {
case StateVersion.One:
await this.migrateClientKeys();
await this.migrateStateFrom1To2();
break;
}
currentStateVersion += 1;
}
}
// TODO: remove this migration when we are confident existing api keys are all migrated. Probably 1-2 releases.
protected async migrateClientKeys() {
const oldClientId = await this.storageService.get<string>(ClientKeys.clientIdOld);
const oldClientSecret = await this.storageService.get<string>(ClientKeys.clientSecretOld);
if (oldClientId != null) {
await this.storageService.save(ClientKeys.clientId, oldClientId);
await this.storageService.remove(ClientKeys.clientIdOld);
}
if (oldClientSecret != null) {
await this.storageService.save(ClientKeys.clientSecret, oldClientSecret);
await this.storageService.remove(ClientKeys.clientSecretOld);
}
}
protected async migrateStateFrom1To2(useSecureStorageForSecrets = true): Promise<void> {
// Grabbing a couple of key settings before they get cleared by the base migration
const userId = await this.get<string>(Keys.entityId);
const clientId = await this.get<string>(ClientKeys.clientId);
const clientSecret = await this.get<string>(ClientKeys.clientSecret);
await super.migrateStateFrom1To2();
// Setup reusable method for clearing keys since we will want to do that regardless of if there is an active authenticated session
const clearDirectoryConnectorV1Keys = async () => {
for (const key in Keys) {
if (key == null) {
continue;
}
for (const directoryType in DirectoryType) {
if (directoryType == null) {
continue;
}
await this.set(SecureStorageKeys.directoryConfigPrefix + directoryType, null);
}
}
};
// Initilize typed objects from key/value pairs in storage to either be saved temporarily until an account is authed or applied to the active account
const getDirectoryConfig = async <T>(type: DirectoryType) =>
await this.get<T>(SecureStorageKeys.directoryConfigPrefix + type);
const directoryConfigs: DirectoryConfigurations = {
ldap: await getDirectoryConfig<LdapConfiguration>(DirectoryType.Ldap),
gsuite: await getDirectoryConfig<GSuiteConfiguration>(DirectoryType.GSuite),
azure: await getDirectoryConfig<AzureConfiguration>(DirectoryType.AzureActiveDirectory),
okta: await getDirectoryConfig<OktaConfiguration>(DirectoryType.Okta),
oneLogin: await getDirectoryConfig<OneLoginConfiguration>(DirectoryType.OneLogin),
};
const directorySettings: DirectorySettings = {
directoryType: await this.get<DirectoryType>(Keys.directoryType),
organizationId: await this.get<string>(Keys.organizationId),
lastUserSync: await this.get<Date>(Keys.lastUserSync),
lastGroupSync: await this.get<Date>(Keys.lastGroupSync),
lastSyncHash: await this.get<string>(Keys.lastSyncHash),
syncingDir: await this.get<boolean>(Keys.syncingDir),
sync: await this.get<SyncConfiguration>(Keys.syncConfig),
};
// (userId == null) = no authed account, stored data temporarily to be applied and cleared on next auth
// (userId != null) = authed account known, applied stored data to it and do not save temp data
if (userId == null) {
await this.set(Keys.tempDirectoryConfigs, directoryConfigs);
await this.set(Keys.tempDirectorySettings, directorySettings);
await clearDirectoryConnectorV1Keys();
return;
}
const account = await this.get<Account>(userId);
account.directoryConfigurations = directoryConfigs;
account.directorySettings = directorySettings;
account.profile = {
userId: userId,
entityId: userId,
apiKeyClientId: clientId,
};
account.clientKeys = {
clientId: clientId,
clientSecret: clientSecret,
};
await this.set(userId, account);
await clearDirectoryConnectorV1Keys();
if (useSecureStorageForSecrets) {
for (const key in SecureStorageKeys) {
if (await this.secureStorageService.has(SecureStorageKeys[key])) {
await this.secureStorageService.save(
`${userId}_${SecureStorageKeys[key]}`,
await this.secureStorageService.get(SecureStorageKeys[key])
);
await this.secureStorageService.remove(SecureStorageKeys[key]);
}
}
}
}
}