Fix migration to Key Connector (#452)
* Move Key Connector check to subclass * Move authService.logout call to main program * Move Key Connector migration check to unlock command * Use get/setConvertAccountRequired flag * Move Key Connector convert to own command, set usesKeyConnector after conversion * Remove KC conversion check from syncCommand, fix callback * Make class service private * Fix naming convention * Update jslib and deps
This commit is contained in:
parent
922cd1dc54
commit
8b650666c5
2
jslib
2
jslib
|
@ -1 +1 @@
|
||||||
Subproject commit cc285e5ea729795812d8f1f66f622b67c11efb4a
|
Subproject commit 11e7133aefa4443e07febde9b3add25539ec2287
|
|
@ -50,6 +50,8 @@ import { Program } from "./program";
|
||||||
import { SendProgram } from "./send.program";
|
import { SendProgram } from "./send.program";
|
||||||
import { VaultProgram } from "./vault.program";
|
import { VaultProgram } from "./vault.program";
|
||||||
|
|
||||||
|
import { Account, AccountFactory } from "jslib-common/models/domain/account";
|
||||||
|
|
||||||
// Polyfills
|
// Polyfills
|
||||||
(global as any).DOMParser = new jsdom.JSDOM().window.DOMParser;
|
(global as any).DOMParser = new jsdom.JSDOM().window.DOMParser;
|
||||||
|
|
||||||
|
@ -136,7 +138,8 @@ export class Main {
|
||||||
this.storageService,
|
this.storageService,
|
||||||
this.secureStorageService,
|
this.secureStorageService,
|
||||||
this.logService,
|
this.logService,
|
||||||
this.stateMigrationService
|
this.stateMigrationService,
|
||||||
|
new AccountFactory(Account)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.cryptoService = new CryptoService(
|
this.cryptoService = new CryptoService(
|
||||||
|
@ -328,6 +331,9 @@ export class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
async logout() {
|
async logout() {
|
||||||
|
this.authService.logOut(() => {
|
||||||
|
/* Do nothing */
|
||||||
|
});
|
||||||
const userId = await this.stateService.getUserId();
|
const userId = await this.stateService.getUserId();
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.syncService.setLastSync(new Date(0)),
|
this.syncService.setLastSync(new Date(0)),
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
import * as inquirer from "inquirer";
|
||||||
|
|
||||||
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
|
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||||
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
|
|
||||||
|
import { Response } from "jslib-node/cli/models/response";
|
||||||
|
import { MessageResponse } from "jslib-node/cli/models/response/messageResponse";
|
||||||
|
|
||||||
|
export class ConvertToKeyConnectorCommand {
|
||||||
|
constructor(
|
||||||
|
private apiService: ApiService,
|
||||||
|
private keyConnectorService: KeyConnectorService,
|
||||||
|
private environmentService: EnvironmentService,
|
||||||
|
private syncService: SyncService,
|
||||||
|
private logout: () => Promise<void>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async run(): Promise<Response> {
|
||||||
|
// If no interaction available, alert user to use web vault
|
||||||
|
const canInteract = process.env.BW_NOINTERACTION !== "true";
|
||||||
|
if (!canInteract) {
|
||||||
|
await this.logout();
|
||||||
|
return Response.error(
|
||||||
|
new MessageResponse(
|
||||||
|
"An organization you are a member of is using Key Connector. " +
|
||||||
|
"In order to access the vault, you must opt-in to Key Connector now via the web vault. You have been logged out.",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const organization = await this.keyConnectorService.getManagingOrganization();
|
||||||
|
|
||||||
|
const answer: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({
|
||||||
|
type: "list",
|
||||||
|
name: "convert",
|
||||||
|
message:
|
||||||
|
organization.name +
|
||||||
|
" is using a self-hosted key server. A master password is no longer required to log in for members of this organization. ",
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: "Remove master password and unlock",
|
||||||
|
value: "remove",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Leave organization and unlock",
|
||||||
|
value: "leave",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Log out",
|
||||||
|
value: "exit",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (answer.convert === "remove") {
|
||||||
|
try {
|
||||||
|
await this.keyConnectorService.migrateUser();
|
||||||
|
} catch (e) {
|
||||||
|
await this.logout();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.keyConnectorService.removeConvertAccountRequired();
|
||||||
|
await this.keyConnectorService.setUsesKeyConnector(true);
|
||||||
|
|
||||||
|
// Update environment URL - required for api key login
|
||||||
|
const urls = this.environmentService.getUrls();
|
||||||
|
urls.keyConnector = organization.keyConnectorUrl;
|
||||||
|
await this.environmentService.setUrls(urls, true);
|
||||||
|
|
||||||
|
return Response.success();
|
||||||
|
} else if (answer.convert === "leave") {
|
||||||
|
await this.apiService.postLeaveOrganization(organization.id);
|
||||||
|
await this.keyConnectorService.removeConvertAccountRequired();
|
||||||
|
await this.syncService.fullSync(true);
|
||||||
|
return Response.success();
|
||||||
|
} else {
|
||||||
|
await this.logout();
|
||||||
|
return Response.error("You have been logged out.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import * as program from "commander";
|
import * as program from "commander";
|
||||||
import * as inquirer from "inquirer";
|
|
||||||
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
import { AuthService } from "jslib-common/abstractions/auth.service";
|
||||||
|
@ -27,7 +26,6 @@ export class LoginCommand extends BaseLoginCommand {
|
||||||
authService: AuthService,
|
authService: AuthService,
|
||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
cryptoFunctionService: CryptoFunctionService,
|
cryptoFunctionService: CryptoFunctionService,
|
||||||
syncService: SyncService,
|
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
environmentService: EnvironmentService,
|
environmentService: EnvironmentService,
|
||||||
passwordGenerationService: PasswordGenerationService,
|
passwordGenerationService: PasswordGenerationService,
|
||||||
|
@ -35,7 +33,8 @@ export class LoginCommand extends BaseLoginCommand {
|
||||||
stateService: StateService,
|
stateService: StateService,
|
||||||
cryptoService: CryptoService,
|
cryptoService: CryptoService,
|
||||||
policyService: PolicyService,
|
policyService: PolicyService,
|
||||||
keyConnectorService: KeyConnectorService,
|
private syncService: SyncService,
|
||||||
|
private keyConnectorService: KeyConnectorService,
|
||||||
private logoutCallback: () => Promise<void>
|
private logoutCallback: () => Promise<void>
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
|
@ -49,9 +48,7 @@ export class LoginCommand extends BaseLoginCommand {
|
||||||
stateService,
|
stateService,
|
||||||
cryptoService,
|
cryptoService,
|
||||||
policyService,
|
policyService,
|
||||||
"cli",
|
"cli"
|
||||||
syncService,
|
|
||||||
keyConnectorService
|
|
||||||
);
|
);
|
||||||
this.logout = this.logoutCallback;
|
this.logout = this.logoutCallback;
|
||||||
this.validatedParams = async () => {
|
this.validatedParams = async () => {
|
||||||
|
@ -59,6 +56,8 @@ export class LoginCommand extends BaseLoginCommand {
|
||||||
process.env.BW_SESSION = Utils.fromBufferToB64(key);
|
process.env.BW_SESSION = Utils.fromBufferToB64(key);
|
||||||
};
|
};
|
||||||
this.success = async () => {
|
this.success = async () => {
|
||||||
|
await this.syncService.fullSync(true);
|
||||||
|
|
||||||
const usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
const usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -107,7 +107,11 @@ export class ServeCommand {
|
||||||
this.main.stateService,
|
this.main.stateService,
|
||||||
this.main.cryptoFunctionService,
|
this.main.cryptoFunctionService,
|
||||||
this.main.apiService,
|
this.main.apiService,
|
||||||
this.main.logService
|
this.main.logService,
|
||||||
|
this.main.keyConnectorService,
|
||||||
|
this.main.environmentService,
|
||||||
|
this.main.syncService,
|
||||||
|
async () => await this.main.logout()
|
||||||
);
|
);
|
||||||
|
|
||||||
this.sendCreateCommand = new SendCreateCommand(
|
this.sendCreateCommand = new SendCreateCommand(
|
||||||
|
|
|
@ -3,7 +3,10 @@ import * as inquirer from "inquirer";
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
|
||||||
|
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
|
||||||
|
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
|
|
||||||
import { Response } from "jslib-node/cli/models/response";
|
import { Response } from "jslib-node/cli/models/response";
|
||||||
import { MessageResponse } from "jslib-node/cli/models/response/messageResponse";
|
import { MessageResponse } from "jslib-node/cli/models/response/messageResponse";
|
||||||
|
@ -16,13 +19,19 @@ import { HashPurpose } from "jslib-common/enums/hashPurpose";
|
||||||
import { NodeUtils } from "jslib-common/misc/nodeUtils";
|
import { NodeUtils } from "jslib-common/misc/nodeUtils";
|
||||||
import { ConsoleLogService } from "jslib-common/services/consoleLog.service";
|
import { ConsoleLogService } from "jslib-common/services/consoleLog.service";
|
||||||
|
|
||||||
|
import { ConvertToKeyConnectorCommand } from "./convertToKeyConnector.command";
|
||||||
|
|
||||||
export class UnlockCommand {
|
export class UnlockCommand {
|
||||||
constructor(
|
constructor(
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private cryptoFunctionService: CryptoFunctionService,
|
private cryptoFunctionService: CryptoFunctionService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private logService: ConsoleLogService
|
private logService: ConsoleLogService,
|
||||||
|
private keyConnectorService: KeyConnectorService,
|
||||||
|
private environmentService: EnvironmentService,
|
||||||
|
private syncService: SyncService,
|
||||||
|
private logout: () => Promise<void>
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async run(password: string, cmdOptions: Record<string, any>) {
|
async run(password: string, cmdOptions: Record<string, any>) {
|
||||||
|
@ -92,6 +101,33 @@ export class UnlockCommand {
|
||||||
|
|
||||||
if (passwordValid) {
|
if (passwordValid) {
|
||||||
await this.cryptoService.setKey(key);
|
await this.cryptoService.setKey(key);
|
||||||
|
|
||||||
|
if (await this.keyConnectorService.getConvertAccountRequired()) {
|
||||||
|
const convertToKeyConnectorCommand = new ConvertToKeyConnectorCommand(
|
||||||
|
this.apiService,
|
||||||
|
this.keyConnectorService,
|
||||||
|
this.environmentService,
|
||||||
|
this.syncService,
|
||||||
|
this.logout
|
||||||
|
);
|
||||||
|
const convertResponse = await convertToKeyConnectorCommand.run();
|
||||||
|
if (!convertResponse.success) {
|
||||||
|
return convertResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.successResponse();
|
||||||
|
} else {
|
||||||
|
return Response.error("Invalid master password.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setNewSessionKey() {
|
||||||
|
const key = await this.cryptoFunctionService.randomBytes(64);
|
||||||
|
process.env.BW_SESSION = Utils.fromBufferToB64(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async successResponse() {
|
||||||
const res = new MessageResponse(
|
const res = new MessageResponse(
|
||||||
"Your vault is now unlocked!",
|
"Your vault is now unlocked!",
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -108,14 +144,6 @@ export class UnlockCommand {
|
||||||
);
|
);
|
||||||
res.raw = process.env.BW_SESSION;
|
res.raw = process.env.BW_SESSION;
|
||||||
return Response.success(res);
|
return Response.success(res);
|
||||||
} else {
|
|
||||||
return Response.error("Invalid master password.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setNewSessionKey() {
|
|
||||||
const key = await this.cryptoFunctionService.randomBytes(64);
|
|
||||||
process.env.BW_SESSION = Utils.fromBufferToB64(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,6 @@ export class Program extends BaseProgram {
|
||||||
this.main.authService,
|
this.main.authService,
|
||||||
this.main.apiService,
|
this.main.apiService,
|
||||||
this.main.cryptoFunctionService,
|
this.main.cryptoFunctionService,
|
||||||
this.main.syncService,
|
|
||||||
this.main.i18nService,
|
this.main.i18nService,
|
||||||
this.main.environmentService,
|
this.main.environmentService,
|
||||||
this.main.passwordGenerationService,
|
this.main.passwordGenerationService,
|
||||||
|
@ -157,6 +156,7 @@ export class Program extends BaseProgram {
|
||||||
this.main.stateService,
|
this.main.stateService,
|
||||||
this.main.cryptoService,
|
this.main.cryptoService,
|
||||||
this.main.policyService,
|
this.main.policyService,
|
||||||
|
this.main.syncService,
|
||||||
this.main.keyConnectorService,
|
this.main.keyConnectorService,
|
||||||
async () => await this.main.logout()
|
async () => await this.main.logout()
|
||||||
);
|
);
|
||||||
|
@ -257,7 +257,11 @@ export class Program extends BaseProgram {
|
||||||
this.main.stateService,
|
this.main.stateService,
|
||||||
this.main.cryptoFunctionService,
|
this.main.cryptoFunctionService,
|
||||||
this.main.apiService,
|
this.main.apiService,
|
||||||
this.main.logService
|
this.main.logService,
|
||||||
|
this.main.keyConnectorService,
|
||||||
|
this.main.environmentService,
|
||||||
|
this.main.syncService,
|
||||||
|
async () => await this.main.logout()
|
||||||
);
|
);
|
||||||
const response = await command.run(password, cmd);
|
const response = await command.run(password, cmd);
|
||||||
this.processResponse(response);
|
this.processResponse(response);
|
||||||
|
@ -511,7 +515,11 @@ export class Program extends BaseProgram {
|
||||||
this.main.stateService,
|
this.main.stateService,
|
||||||
this.main.cryptoFunctionService,
|
this.main.cryptoFunctionService,
|
||||||
this.main.apiService,
|
this.main.apiService,
|
||||||
this.main.logService
|
this.main.logService,
|
||||||
|
this.main.keyConnectorService,
|
||||||
|
this.main.environmentService,
|
||||||
|
this.main.syncService,
|
||||||
|
this.main.logout
|
||||||
);
|
);
|
||||||
const response = await command.run(null, null);
|
const response = await command.run(null, null);
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
|
Loading…
Reference in New Issue