mirror of
https://github.com/bitwarden/browser
synced 2026-02-12 14:34:02 +00:00
Merge branch 'main' of github.com:bitwarden/clients into arch/kiro
This commit is contained in:
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -94,6 +94,8 @@ libs/platform @bitwarden/team-platform-dev
|
||||
libs/storage-core @bitwarden/team-platform-dev
|
||||
libs/logging @bitwarden/team-platform-dev
|
||||
libs/storage-test-utils @bitwarden/team-platform-dev
|
||||
libs/messaging @bitwarden/team-platform-dev
|
||||
libs/messaging-internal @bitwarden/team-platform-dev
|
||||
# Web utils used across app and connectors
|
||||
apps/web/src/utils/ @bitwarden/team-platform-dev
|
||||
# Web core and shared files
|
||||
|
||||
3
.github/workflows/build-browser-target.yml
vendored
3
.github/workflows/build-browser-target.yml
vendored
@@ -38,6 +38,7 @@ jobs:
|
||||
uses: ./.github/workflows/build-browser.yml
|
||||
secrets: inherit
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
|
||||
|
||||
3
.github/workflows/build-desktop-target.yml
vendored
3
.github/workflows/build-desktop-target.yml
vendored
@@ -38,6 +38,7 @@ jobs:
|
||||
uses: ./.github/workflows/build-desktop.yml
|
||||
secrets: inherit
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
|
||||
|
||||
3
.github/workflows/build-web-target.yml
vendored
3
.github/workflows/build-web-target.yml
vendored
@@ -37,7 +37,8 @@ jobs:
|
||||
uses: ./.github/workflows/build-web.yml
|
||||
secrets: inherit
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
security-events: write
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { firstValueFrom, switchMap } from "rxjs";
|
||||
import { filter, firstValueFrom, of, switchMap } from "rxjs";
|
||||
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
@@ -51,9 +51,14 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
|
||||
* Initializes the auto-submit login policy. If the policy is not enabled, it
|
||||
* will trigger a removal of any established listeners.
|
||||
*/
|
||||
|
||||
async init() {
|
||||
this.accountService.activeAccount$
|
||||
this.authService.activeAccountStatus$
|
||||
.pipe(
|
||||
switchMap((value) =>
|
||||
value === AuthenticationStatus.Unlocked ? this.accountService.activeAccount$ : of(null),
|
||||
),
|
||||
filter((account): account is Account => account !== null),
|
||||
getUserId,
|
||||
switchMap((userId) =>
|
||||
this.policyService.policiesByType$(PolicyType.AutomaticAppLogIn, userId),
|
||||
|
||||
@@ -3,10 +3,8 @@ import { Subject, firstValueFrom } from "rxjs";
|
||||
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { tagAsExternal } from "@bitwarden/common/platform/messaging/helpers";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { tagAsExternal } from "@bitwarden/messaging-internal";
|
||||
|
||||
import { FullSyncMessage } from "./foreground-sync.service";
|
||||
import { FULL_SYNC_FINISHED, SyncServiceListener } from "./sync-service.listener";
|
||||
|
||||
@@ -146,14 +146,16 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||
ciphers: PopupCipherViewLike[];
|
||||
}[]
|
||||
>(() => {
|
||||
const ciphers = this.ciphers();
|
||||
|
||||
// Not grouping by type, return a single group with all ciphers
|
||||
if (!this.groupByType()) {
|
||||
return [{ ciphers: this.ciphers() }];
|
||||
if (!this.groupByType() && ciphers.length > 0) {
|
||||
return [{ ciphers }];
|
||||
}
|
||||
|
||||
const groups: Record<string, PopupCipherViewLike[]> = {};
|
||||
|
||||
this.ciphers().forEach((cipher) => {
|
||||
ciphers.forEach((cipher) => {
|
||||
let groupKey = "all";
|
||||
switch (CipherViewLikeUtils.getType(cipher)) {
|
||||
case CipherType.Card:
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"browser-hrtime": "1.1.8",
|
||||
"chalk": "4.1.2",
|
||||
"commander": "11.1.0",
|
||||
"core-js": "3.42.0",
|
||||
"core-js": "3.44.0",
|
||||
"form-data": "4.0.2",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"inquirer": "8.2.6",
|
||||
|
||||
@@ -25,7 +25,6 @@ import { LoginExport } from "@bitwarden/common/models/export/login.export";
|
||||
import { SecureNoteExport } from "@bitwarden/common/models/export/secure-note.export";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
@@ -44,7 +43,6 @@ import { SelectionReadOnly } from "../admin-console/models/selection-read-only";
|
||||
import { Response } from "../models/response";
|
||||
import { StringResponse } from "../models/response/string.response";
|
||||
import { TemplateResponse } from "../models/response/template.response";
|
||||
import { SendResponse } from "../tools/send/models/send.response";
|
||||
import { CliUtils } from "../utils";
|
||||
import { CipherResponse } from "../vault/models/cipher.response";
|
||||
import { FolderResponse } from "../vault/models/folder.response";
|
||||
@@ -577,11 +575,11 @@ export class GetCommand extends DownloadCommand {
|
||||
case "org-collection":
|
||||
template = OrganizationCollectionRequest.template();
|
||||
break;
|
||||
case "send.text":
|
||||
template = SendResponse.template(SendType.Text);
|
||||
break;
|
||||
case "send.file":
|
||||
template = SendResponse.template(SendType.File);
|
||||
case "send.text":
|
||||
template = Response.badRequest(
|
||||
`Invalid template object. Use \`bw send template ${id}\` instead.`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return Response.badRequest("Unknown template object.");
|
||||
|
||||
@@ -76,9 +76,14 @@ export class SendCreateCommand {
|
||||
const filePath = req.file?.fileName ?? options.file;
|
||||
const text = req.text?.text ?? options.text;
|
||||
const hidden = req.text?.hidden ?? options.hidden;
|
||||
const password = req.password ?? options.password;
|
||||
const password = req.password ?? options.password ?? undefined;
|
||||
const emails = req.emails ?? options.emails ?? undefined;
|
||||
const maxAccessCount = req.maxAccessCount ?? options.maxAccessCount;
|
||||
|
||||
if (emails !== undefined && password !== undefined) {
|
||||
return Response.badRequest("--password and --emails are mutually exclusive.");
|
||||
}
|
||||
|
||||
req.key = null;
|
||||
req.maxAccessCount = maxAccessCount;
|
||||
|
||||
@@ -133,6 +138,7 @@ export class SendCreateCommand {
|
||||
// Add dates from template
|
||||
encSend.deletionDate = sendView.deletionDate;
|
||||
encSend.expirationDate = sendView.expirationDate;
|
||||
encSend.emails = emails && emails.join(",");
|
||||
|
||||
await this.sendApiService.save([encSend, fileData]);
|
||||
const newSend = await this.sendService.getFromState(encSend.id);
|
||||
@@ -151,12 +157,14 @@ class Options {
|
||||
text: string;
|
||||
maxAccessCount: number;
|
||||
password: string;
|
||||
emails: Array<string>;
|
||||
hidden: boolean;
|
||||
|
||||
constructor(passedOptions: Record<string, any>) {
|
||||
this.file = passedOptions?.file;
|
||||
this.text = passedOptions?.text;
|
||||
this.password = passedOptions?.password;
|
||||
this.emails = passedOptions?.email;
|
||||
this.hidden = CliUtils.convertBooleanOption(passedOptions?.hidden);
|
||||
this.maxAccessCount =
|
||||
passedOptions?.maxAccessCount != null ? parseInt(passedOptions.maxAccessCount, null) : null;
|
||||
|
||||
@@ -50,11 +50,21 @@ export class SendEditCommand {
|
||||
|
||||
const normalizedOptions = new Options(cmdOptions);
|
||||
req.id = normalizedOptions.itemId || req.id;
|
||||
|
||||
if (req.id != null) {
|
||||
req.id = req.id.toLowerCase();
|
||||
if (normalizedOptions.emails) {
|
||||
req.emails = normalizedOptions.emails;
|
||||
req.password = undefined;
|
||||
} else if (normalizedOptions.password) {
|
||||
req.emails = undefined;
|
||||
req.password = normalizedOptions.password;
|
||||
} else if (req.password && (typeof req.password !== "string" || req.password === "")) {
|
||||
req.password = undefined;
|
||||
}
|
||||
|
||||
if (!req.id) {
|
||||
return Response.error("`itemid` was not provided.");
|
||||
}
|
||||
|
||||
req.id = req.id.toLowerCase();
|
||||
const send = await this.sendService.getFromState(req.id);
|
||||
|
||||
if (send == null) {
|
||||
@@ -76,10 +86,6 @@ export class SendEditCommand {
|
||||
let sendView = await send.decrypt();
|
||||
sendView = SendResponse.toView(req, sendView);
|
||||
|
||||
if (typeof req.password !== "string" || req.password === "") {
|
||||
req.password = null;
|
||||
}
|
||||
|
||||
try {
|
||||
const [encSend, encFileData] = await this.sendService.encrypt(sendView, null, req.password);
|
||||
// Add dates from template
|
||||
@@ -97,8 +103,12 @@ export class SendEditCommand {
|
||||
|
||||
class Options {
|
||||
itemId: string;
|
||||
password: string;
|
||||
emails: string[];
|
||||
|
||||
constructor(passedOptions: Record<string, any>) {
|
||||
this.itemId = passedOptions?.itemId || passedOptions?.itemid;
|
||||
this.password = passedOptions.password;
|
||||
this.emails = passedOptions.email;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,4 @@ export * from "./get.command";
|
||||
export * from "./list.command";
|
||||
export * from "./receive.command";
|
||||
export * from "./remove-password.command";
|
||||
export * from "./template.command";
|
||||
|
||||
35
apps/cli/src/tools/send/commands/template.command.ts
Normal file
35
apps/cli/src/tools/send/commands/template.command.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
|
||||
import { Response } from "../../../models/response";
|
||||
import { TemplateResponse } from "../../../models/response/template.response";
|
||||
import { SendResponse } from "../models/send.response";
|
||||
|
||||
export class SendTemplateCommand {
|
||||
constructor() {}
|
||||
|
||||
run(type: string): Response {
|
||||
let template: SendResponse | undefined;
|
||||
let response: Response;
|
||||
|
||||
switch (type) {
|
||||
case "send.text":
|
||||
case "text":
|
||||
template = SendResponse.template(SendType.Text);
|
||||
break;
|
||||
case "send.file":
|
||||
case "file":
|
||||
template = SendResponse.template(SendType.File);
|
||||
break;
|
||||
default:
|
||||
response = Response.badRequest("Unknown template object.");
|
||||
}
|
||||
|
||||
if (template) {
|
||||
response = Response.success(new TemplateResponse(template));
|
||||
}
|
||||
|
||||
response ??= Response.badRequest("An error occurred while retrieving the template.");
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ export class SendResponse implements BaseResponse {
|
||||
req.deletionDate = this.getStandardDeletionDate(deleteInDays);
|
||||
req.expirationDate = null;
|
||||
req.password = null;
|
||||
req.emails = null;
|
||||
req.disabled = false;
|
||||
req.hideEmail = false;
|
||||
return req;
|
||||
@@ -50,6 +51,7 @@ export class SendResponse implements BaseResponse {
|
||||
view.deletionDate = send.deletionDate;
|
||||
view.expirationDate = send.expirationDate;
|
||||
view.password = send.password;
|
||||
view.emails = send.emails ?? [];
|
||||
view.disabled = send.disabled;
|
||||
view.hideEmail = send.hideEmail;
|
||||
return view;
|
||||
@@ -87,6 +89,7 @@ export class SendResponse implements BaseResponse {
|
||||
expirationDate: Date;
|
||||
password: string;
|
||||
passwordSet: boolean;
|
||||
emails?: Array<string>;
|
||||
disabled: boolean;
|
||||
hideEmail: boolean;
|
||||
|
||||
|
||||
@@ -4,13 +4,12 @@ import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import * as chalk from "chalk";
|
||||
import { program, Command, OptionValues } from "commander";
|
||||
import { program, Command, Option, OptionValues } from "commander";
|
||||
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||
|
||||
import { BaseProgram } from "../../base-program";
|
||||
import { GetCommand } from "../../commands/get.command";
|
||||
import { Response } from "../../models/response";
|
||||
import { CliUtils } from "../../utils";
|
||||
|
||||
@@ -22,10 +21,12 @@ import {
|
||||
SendListCommand,
|
||||
SendReceiveCommand,
|
||||
SendRemovePasswordCommand,
|
||||
SendTemplateCommand,
|
||||
} from "./commands";
|
||||
import { SendFileResponse } from "./models/send-file.response";
|
||||
import { SendTextResponse } from "./models/send-text.response";
|
||||
import { SendResponse } from "./models/send.response";
|
||||
import { parseEmail } from "./util";
|
||||
|
||||
const writeLn = CliUtils.writeLn;
|
||||
|
||||
@@ -48,6 +49,17 @@ export class SendProgram extends BaseProgram {
|
||||
"The number of days in the future to set deletion date, defaults to 7",
|
||||
"7",
|
||||
)
|
||||
.addOption(
|
||||
new Option(
|
||||
"--password <password>",
|
||||
"optional password to access this Send. Can also be specified in JSON.",
|
||||
).conflicts("email"),
|
||||
)
|
||||
.option(
|
||||
"--email <email>",
|
||||
"optional emails to access this Send. Can also be specified in JSON.",
|
||||
parseEmail,
|
||||
)
|
||||
.option("-a, --maxAccessCount <amount>", "The amount of max possible accesses.")
|
||||
.option("--hidden", "Hide <data> in web by default. Valid only if --file is not set.")
|
||||
.option(
|
||||
@@ -139,26 +151,9 @@ export class SendProgram extends BaseProgram {
|
||||
return new Command("template")
|
||||
.argument("<object>", "Valid objects are: send.text, send.file")
|
||||
.description("Get json templates for send objects")
|
||||
.action(async (object) => {
|
||||
const cmd = new GetCommand(
|
||||
this.serviceContainer.cipherService,
|
||||
this.serviceContainer.folderService,
|
||||
this.serviceContainer.collectionService,
|
||||
this.serviceContainer.totpService,
|
||||
this.serviceContainer.auditService,
|
||||
this.serviceContainer.keyService,
|
||||
this.serviceContainer.encryptService,
|
||||
this.serviceContainer.searchService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.organizationService,
|
||||
this.serviceContainer.eventCollectionService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.cliRestrictedItemTypesService,
|
||||
);
|
||||
const response = await cmd.run("template", object, null);
|
||||
this.processResponse(response);
|
||||
});
|
||||
.action((options: OptionValues) =>
|
||||
this.processResponse(new SendTemplateCommand().run(options.object)),
|
||||
);
|
||||
}
|
||||
|
||||
private getCommand(): Command {
|
||||
@@ -208,10 +203,6 @@ export class SendProgram extends BaseProgram {
|
||||
.option("--file <path>", "file to Send. Can also be specified in parent's JSON.")
|
||||
.option("--text <text>", "text to Send. Can also be specified in parent's JSON.")
|
||||
.option("--hidden", "text hidden flag. Valid only with the --text option.")
|
||||
.option(
|
||||
"--password <password>",
|
||||
"optional password to access this Send. Can also be specified in JSON",
|
||||
)
|
||||
.on("--help", () => {
|
||||
writeLn("");
|
||||
writeLn("Note:");
|
||||
@@ -219,13 +210,13 @@ export class SendProgram extends BaseProgram {
|
||||
writeLn("", true);
|
||||
})
|
||||
.action(async (encodedJson: string, options: OptionValues, args: { parent: Command }) => {
|
||||
// Work-around to support `--fullObject` option for `send create --fullObject`
|
||||
// Calling `option('--fullObject', ...)` above won't work due to Commander doesn't like same option
|
||||
// to be defind on both parent-command and sub-command
|
||||
const { fullObject = false } = args.parent.opts();
|
||||
// subcommands inherit flags from their parent; they cannot override them
|
||||
const { fullObject = false, email = undefined, password = undefined } = args.parent.opts();
|
||||
const mergedOptions = {
|
||||
...options,
|
||||
fullObject: fullObject,
|
||||
email,
|
||||
password,
|
||||
};
|
||||
|
||||
const response = await this.runCreate(encodedJson, mergedOptions);
|
||||
@@ -247,7 +238,7 @@ export class SendProgram extends BaseProgram {
|
||||
writeLn(" You cannot update a File-type Send's file. Just delete and remake it");
|
||||
writeLn("", true);
|
||||
})
|
||||
.action(async (encodedJson: string, options: OptionValues) => {
|
||||
.action(async (encodedJson: string, options: OptionValues, args: { parent: Command }) => {
|
||||
await this.exitIfLocked();
|
||||
const getCmd = new SendGetCommand(
|
||||
this.serviceContainer.sendService,
|
||||
@@ -264,7 +255,16 @@ export class SendProgram extends BaseProgram {
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
const response = await cmd.run(encodedJson, options);
|
||||
|
||||
// subcommands inherit flags from their parent; they cannot override them
|
||||
const { email = undefined, password = undefined } = args.parent.opts();
|
||||
const mergedOptions = {
|
||||
...options,
|
||||
email,
|
||||
password,
|
||||
};
|
||||
|
||||
const response = await cmd.run(encodedJson, mergedOptions);
|
||||
this.processResponse(response);
|
||||
});
|
||||
}
|
||||
|
||||
194
apps/cli/src/tools/send/util.spec.ts
Normal file
194
apps/cli/src/tools/send/util.spec.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
import { parseEmail } from "./util";
|
||||
|
||||
describe("parseEmail", () => {
|
||||
describe("single email address parsing", () => {
|
||||
it("should parse a valid single email address", () => {
|
||||
const result = parseEmail("test@example.com", []);
|
||||
expect(result).toEqual(["test@example.com"]);
|
||||
});
|
||||
|
||||
it("should parse email with dots in local part", () => {
|
||||
const result = parseEmail("test.user@example.com", []);
|
||||
expect(result).toEqual(["test.user@example.com"]);
|
||||
});
|
||||
|
||||
it("should parse email with underscores and hyphens", () => {
|
||||
const result = parseEmail("test_user-name@example.com", []);
|
||||
expect(result).toEqual(["test_user-name@example.com"]);
|
||||
});
|
||||
|
||||
it("should parse email with plus sign", () => {
|
||||
const result = parseEmail("test+user@example.com", []);
|
||||
expect(result).toEqual(["test+user@example.com"]);
|
||||
});
|
||||
|
||||
it("should parse email with dots and hyphens in domain", () => {
|
||||
const result = parseEmail("user@test-domain.co.uk", []);
|
||||
expect(result).toEqual(["user@test-domain.co.uk"]);
|
||||
});
|
||||
|
||||
it("should add single email to existing previousInput array", () => {
|
||||
const result = parseEmail("new@example.com", ["existing@test.com"]);
|
||||
expect(result).toEqual(["existing@test.com", "new@example.com"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("comma-separated email lists", () => {
|
||||
it("should parse comma-separated email list", () => {
|
||||
const result = parseEmail("test@example.com,user@domain.com", []);
|
||||
expect(result).toEqual(["test@example.com", "user@domain.com"]);
|
||||
});
|
||||
|
||||
it("should parse comma-separated emails with spaces", () => {
|
||||
const result = parseEmail("test@example.com, user@domain.com, admin@site.org", []);
|
||||
expect(result).toEqual(["test@example.com", "user@domain.com", "admin@site.org"]);
|
||||
});
|
||||
|
||||
it("should combine comma-separated emails with previousInput", () => {
|
||||
const result = parseEmail("new1@example.com,new2@domain.com", ["existing@test.com"]);
|
||||
expect(result).toEqual(["existing@test.com", "new1@example.com", "new2@domain.com"]);
|
||||
});
|
||||
|
||||
it("should throw error for invalid email in comma-separated list", () => {
|
||||
expect(() => {
|
||||
parseEmail("valid@example.com,invalid-email,another@domain.com", []);
|
||||
}).toThrow("Invalid email address: invalid-email");
|
||||
});
|
||||
});
|
||||
|
||||
describe("space-separated email lists", () => {
|
||||
it("should parse space-separated email list", () => {
|
||||
const result = parseEmail("test@example.com user@domain.com", []);
|
||||
expect(result).toEqual(["test@example.com", "user@domain.com"]);
|
||||
});
|
||||
|
||||
it("should parse space-separated emails with multiple spaces", () => {
|
||||
const result = parseEmail("test@example.com user@domain.com admin@site.org", []);
|
||||
expect(result).toEqual(["test@example.com", "user@domain.com", "admin@site.org"]);
|
||||
});
|
||||
|
||||
it("should combine space-separated emails with previousInput", () => {
|
||||
const result = parseEmail("new1@example.com new2@domain.com", ["existing@test.com"]);
|
||||
expect(result).toEqual(["existing@test.com", "new1@example.com", "new2@domain.com"]);
|
||||
});
|
||||
|
||||
it("should throw error for invalid email in space-separated list", () => {
|
||||
expect(() => {
|
||||
parseEmail("valid@example.com invalid-email another@domain.com", []);
|
||||
}).toThrow("Invalid email address: invalid-email");
|
||||
});
|
||||
});
|
||||
|
||||
describe("JSON array input format", () => {
|
||||
it("should parse valid JSON array of emails", () => {
|
||||
const result = parseEmail('["test@example.com", "user@domain.com"]', []);
|
||||
expect(result).toEqual(["test@example.com", "user@domain.com"]);
|
||||
});
|
||||
|
||||
it("should parse single email in JSON array", () => {
|
||||
const result = parseEmail('["test@example.com"]', []);
|
||||
expect(result).toEqual(["test@example.com"]);
|
||||
});
|
||||
|
||||
it("should parse empty JSON array", () => {
|
||||
const result = parseEmail("[]", []);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("should combine JSON array with previousInput", () => {
|
||||
const result = parseEmail('["new1@example.com", "new2@domain.com"]', ["existing@test.com"]);
|
||||
expect(result).toEqual(["existing@test.com", "new1@example.com", "new2@domain.com"]);
|
||||
});
|
||||
|
||||
it("should throw error for malformed JSON", () => {
|
||||
expect(() => {
|
||||
parseEmail('["test@example.com", "user@domain.com"', []);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it("should throw error for JSON that is not an array", () => {
|
||||
expect(() => {
|
||||
parseEmail('{"email": "test@example.com"}', []);
|
||||
}).toThrow("Invalid email address:");
|
||||
});
|
||||
|
||||
it("should throw error for JSON string instead of array", () => {
|
||||
expect(() => {
|
||||
parseEmail('"test@example.com"', []);
|
||||
}).toThrow("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
});
|
||||
|
||||
it("should throw error for JSON number instead of array", () => {
|
||||
expect(() => {
|
||||
parseEmail("123", []);
|
||||
}).toThrow("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
});
|
||||
});
|
||||
|
||||
describe("`previousInput` parameter handling", () => {
|
||||
it("should handle undefined previousInput", () => {
|
||||
const result = parseEmail("test@example.com", undefined as any);
|
||||
expect(result).toEqual(["test@example.com"]);
|
||||
});
|
||||
|
||||
it("should handle null previousInput", () => {
|
||||
const result = parseEmail("test@example.com", null as any);
|
||||
expect(result).toEqual(["test@example.com"]);
|
||||
});
|
||||
|
||||
it("should preserve existing emails in previousInput", () => {
|
||||
const existing = ["existing1@test.com", "existing2@test.com"];
|
||||
const result = parseEmail("new@example.com", existing);
|
||||
expect(result).toEqual(["existing1@test.com", "existing2@test.com", "new@example.com"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("error cases and edge conditions", () => {
|
||||
it("should throw error for empty string input", () => {
|
||||
expect(() => {
|
||||
parseEmail("", []);
|
||||
}).toThrow("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
});
|
||||
|
||||
it("should return empty array for whitespace-only input", () => {
|
||||
const result = parseEmail(" ", []);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("should throw error for invalid single email", () => {
|
||||
expect(() => {
|
||||
parseEmail("invalid-email", []);
|
||||
}).toThrow("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
});
|
||||
|
||||
it("should throw error for email without @ symbol", () => {
|
||||
expect(() => {
|
||||
parseEmail("testexample.com", []);
|
||||
}).toThrow("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
});
|
||||
|
||||
it("should throw error for email without domain", () => {
|
||||
expect(() => {
|
||||
parseEmail("test@", []);
|
||||
}).toThrow("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
});
|
||||
|
||||
it("should throw error for email without local part", () => {
|
||||
expect(() => {
|
||||
parseEmail("@example.com", []);
|
||||
}).toThrow("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
});
|
||||
|
||||
it("should throw error for input that looks like file path", () => {
|
||||
expect(() => {
|
||||
parseEmail("/path/to/file.txt", []);
|
||||
}).toThrow("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
});
|
||||
|
||||
it("should throw error for input that looks like URL", () => {
|
||||
expect(() => {
|
||||
parseEmail("https://example.com", []);
|
||||
}).toThrow("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
});
|
||||
});
|
||||
});
|
||||
55
apps/cli/src/tools/send/util.ts
Normal file
55
apps/cli/src/tools/send/util.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Parses email addresses from various input formats and combines them with previously parsed emails.
|
||||
*
|
||||
* Supports: single email, JSON array, comma-separated, or space-separated lists.
|
||||
* Note: Function signature follows Commander.js option parsing pattern.
|
||||
*
|
||||
* @param input - Email input string in any supported format
|
||||
* @param previousInput - Previously parsed email addresses to append to
|
||||
* @returns Combined array of email addresses
|
||||
* @throws {Error} For invalid JSON, non-array JSON, invalid email addresses, or unrecognized format
|
||||
*
|
||||
* @example
|
||||
* parseEmail("user@example.com", []) // ["user@example.com"]
|
||||
* parseEmail('["user1@example.com", "user2@example.com"]', []) // ["user1@example.com", "user2@example.com"]
|
||||
* parseEmail("user1@example.com, user2@example.com", []) // ["user1@example.com", "user2@example.com"]
|
||||
*/
|
||||
export function parseEmail(input: string, previousInput: string[]) {
|
||||
let result = previousInput ?? [];
|
||||
|
||||
if (isEmail(input)) {
|
||||
result.push(input);
|
||||
} else if (input.startsWith("[")) {
|
||||
const json = JSON.parse(input);
|
||||
if (!Array.isArray(json)) {
|
||||
throw new Error("invalid JSON");
|
||||
}
|
||||
|
||||
result = result.concat(json);
|
||||
} else if (input.includes(",")) {
|
||||
result = result.concat(parseList(input, ","));
|
||||
} else if (input.includes(" ")) {
|
||||
result = result.concat(parseList(input, " "));
|
||||
} else {
|
||||
throw new Error("`input` must be a single address, a comma-separated list, or a JSON array");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function isEmail(input: string) {
|
||||
return !!input && !!input.match(/^([\w._+-]+?)@([\w._+-]+?)$/);
|
||||
}
|
||||
|
||||
function parseList(value: string, separator: string) {
|
||||
const parts = value
|
||||
.split(separator)
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => !!v.length);
|
||||
const invalid = parts.find((v) => !isEmail(v));
|
||||
if (invalid) {
|
||||
throw new Error(`Invalid email address: ${invalid}`);
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
@@ -240,7 +240,8 @@
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
},
|
||||
"rpm": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
||||
"fpm": ["--rpm-rpmbuild-define", "_build_id_links none"]
|
||||
},
|
||||
"freebsd": {
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@bitwarden/common": "file:../../../libs/common",
|
||||
"@bitwarden/logging": "dist/libs/logging/src",
|
||||
"@bitwarden/node": "file:../../../libs/node",
|
||||
"@bitwarden/storage-core": "file:../../../libs/storage-core",
|
||||
"module-alias": "2.2.3",
|
||||
"ts-node": "10.9.2",
|
||||
"uuid": "11.1.0",
|
||||
@@ -31,14 +33,28 @@
|
||||
"version": "0.0.0",
|
||||
"license": "GPL-3.0"
|
||||
},
|
||||
"../../../libs/storage-core": {
|
||||
"name": "@bitwarden/storage-core",
|
||||
"version": "0.0.1",
|
||||
"license": "GPL-3.0"
|
||||
},
|
||||
"dist/libs/logging/src": {},
|
||||
"node_modules/@bitwarden/common": {
|
||||
"resolved": "../../../libs/common",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/logging": {
|
||||
"resolved": "dist/libs/logging/src",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/node": {
|
||||
"resolved": "../../../libs/node",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/storage-core": {
|
||||
"resolved": "../../../libs/storage-core",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
"dependencies": {
|
||||
"@bitwarden/common": "file:../../../libs/common",
|
||||
"@bitwarden/node": "file:../../../libs/node",
|
||||
"@bitwarden/storage-core": "file:../../../libs/storage-core",
|
||||
"@bitwarden/logging": "dist/libs/logging/src",
|
||||
"module-alias": "2.2.3",
|
||||
"ts-node": "10.9.2",
|
||||
"uuid": "11.1.0",
|
||||
@@ -27,6 +29,8 @@
|
||||
},
|
||||
"_moduleAliases": {
|
||||
"@bitwarden/common": "dist/libs/common/src",
|
||||
"@bitwarden/node/services/node-crypto-function.service": "dist/libs/node/src/services/node-crypto-function.service"
|
||||
"@bitwarden/node/services/node-crypto-function.service": "dist/libs/node/src/services/node-crypto-function.service",
|
||||
"@bitwarden/storage-core": "dist/libs/storage-core/src",
|
||||
"@bitwarden/logging": "dist/libs/logging/src"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "../tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "dist",
|
||||
"target": "es6",
|
||||
"module": "CommonJS",
|
||||
@@ -10,7 +10,15 @@
|
||||
"sourceMap": false,
|
||||
"declaration": false,
|
||||
"paths": {
|
||||
"@src/*": ["src/*"]
|
||||
"@src/*": ["src/*"],
|
||||
"@bitwarden/user-core": ["../../../libs/user-core/src/index.ts"],
|
||||
"@bitwarden/storage-core": ["../../../libs/storage-core/src/index.ts"],
|
||||
"@bitwarden/logging": ["../../../libs/logging/src/index.ts"],
|
||||
"@bitwarden/admin-console/*": ["../../../libs/admin-console/src/*"],
|
||||
"@bitwarden/auth/*": ["../../../libs/auth/src/*"],
|
||||
"@bitwarden/common/*": ["../../../libs/common/src/*"],
|
||||
"@bitwarden/key-management": ["../../../libs/key-management/src/"],
|
||||
"@bitwarden/node/*": ["../../../libs/node/src/*"]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
<app-organization-free-trial-warning
|
||||
[organization]="organization"
|
||||
(clicked)="navigateToPaymentMethod()"
|
||||
>
|
||||
</app-organization-free-trial-warning>
|
||||
<app-header>
|
||||
<bit-search
|
||||
class="tw-grow"
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
Observable,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
tap,
|
||||
} from "rxjs";
|
||||
|
||||
import {
|
||||
@@ -61,6 +62,7 @@ import {
|
||||
ChangePlanDialogResultType,
|
||||
openChangePlanDialog,
|
||||
} from "../../../billing/organizations/change-plan-dialog.component";
|
||||
import { OrganizationWarningsService } from "../../../billing/warnings/services";
|
||||
import { BaseMembersComponent } from "../../common/base-members.component";
|
||||
import { PeopleTableDataSource } from "../../common/people-table-data-source";
|
||||
import { GroupApiService } from "../core";
|
||||
@@ -148,6 +150,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
protected deleteManagedMemberWarningService: DeleteManagedMemberWarningService,
|
||||
private configService: ConfigService,
|
||||
private organizationUserService: OrganizationUserService,
|
||||
private organizationWarningsService: OrganizationWarningsService,
|
||||
) {
|
||||
super(
|
||||
apiService,
|
||||
@@ -247,6 +250,13 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
this.showUserManagementControls$ = organization$.pipe(
|
||||
map((organization) => organization.canManageUsers),
|
||||
);
|
||||
organization$
|
||||
.pipe(
|
||||
takeUntilDestroyed(),
|
||||
tap((org) => (this.organization = org)),
|
||||
switchMap((org) => this.organizationWarningsService.showInactiveSubscriptionDialog$(org)),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
async getUsers(): Promise<OrganizationUserView[]> {
|
||||
@@ -932,4 +942,14 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
.getCheckedUsers()
|
||||
.every((member) => member.managedByOrganization && validStatuses.includes(member.status));
|
||||
}
|
||||
|
||||
async navigateToPaymentMethod() {
|
||||
const managePaymentDetailsOutsideCheckout = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.PM21881_ManagePaymentDetailsOutsideCheckout,
|
||||
);
|
||||
const route = managePaymentDetailsOutsideCheckout ? "payment-details" : "payment-method";
|
||||
await this.router.navigate(["organizations", `${this.organization?.id}`, "billing", route], {
|
||||
state: { launchPaymentModalAutomatically: true },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { PasswordStrengthV2Component } from "@bitwarden/angular/tools/password-s
|
||||
import { PasswordCalloutComponent } from "@bitwarden/auth/angular";
|
||||
import { ScrollLayoutDirective } from "@bitwarden/components";
|
||||
|
||||
import { OrganizationFreeTrialWarningComponent } from "../../../billing/warnings/components";
|
||||
import { LooseComponentsModule } from "../../../shared";
|
||||
import { SharedOrganizationModule } from "../shared";
|
||||
|
||||
@@ -29,6 +30,7 @@ import { MembersComponent } from "./members.component";
|
||||
ScrollingModule,
|
||||
PasswordStrengthV2Component,
|
||||
ScrollLayoutDirective,
|
||||
OrganizationFreeTrialWarningComponent,
|
||||
],
|
||||
declarations: [
|
||||
BulkConfirmDialogComponent,
|
||||
|
||||
@@ -36,6 +36,10 @@ import {
|
||||
AdjustPaymentDialogComponent,
|
||||
AdjustPaymentDialogResultType,
|
||||
} from "../../shared/adjust-payment-dialog/adjust-payment-dialog.component";
|
||||
import {
|
||||
TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE,
|
||||
TrialPaymentDialogComponent,
|
||||
} from "../../shared/trial-payment-dialog/trial-payment-dialog.component";
|
||||
import { FreeTrial } from "../../types/free-trial";
|
||||
|
||||
@Component({
|
||||
@@ -212,15 +216,15 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
|
||||
};
|
||||
|
||||
changePayment = async () => {
|
||||
const dialogRef = AdjustPaymentDialogComponent.open(this.dialogService, {
|
||||
const dialogRef = TrialPaymentDialogComponent.open(this.dialogService, {
|
||||
data: {
|
||||
initialPaymentMethod: this.paymentSource?.type,
|
||||
organizationId: this.organizationId,
|
||||
productTier: this.organization?.productTierType,
|
||||
subscription: this.organizationSubscriptionResponse,
|
||||
productTierType: this.organization?.productTierType,
|
||||
},
|
||||
});
|
||||
const result = await lastValueFrom(dialogRef.closed);
|
||||
if (result === AdjustPaymentDialogResultType.Submitted) {
|
||||
if (result === TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE.SUBMITTED) {
|
||||
this.location.replaceState(this.location.path(), "", {});
|
||||
if (this.launchPaymentModalAutomatically && !this.organization.enabled) {
|
||||
await this.syncService.fullSync(true);
|
||||
|
||||
59
apps/web/src/app/billing/services/plan-card.service.ts
Normal file
59
apps/web/src/app/billing/services/plan-card.service.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
|
||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class PlanCardService {
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
async getCadenceCards(
|
||||
currentPlan: PlanResponse,
|
||||
subscription: OrganizationSubscriptionResponse,
|
||||
isSecretsManagerTrial: boolean,
|
||||
) {
|
||||
const plans = await this.apiService.getPlans();
|
||||
|
||||
const filteredPlans = plans.data.filter((plan) => !!plan.PasswordManager);
|
||||
|
||||
const result =
|
||||
filteredPlans?.filter(
|
||||
(plan) =>
|
||||
plan.productTier === currentPlan.productTier && !plan.disabled && !plan.legacyYear,
|
||||
) || [];
|
||||
|
||||
const planCards = result.map((plan) => {
|
||||
let costPerMember = 0;
|
||||
|
||||
if (plan.PasswordManager.basePrice) {
|
||||
costPerMember = plan.isAnnual
|
||||
? plan.PasswordManager.basePrice / 12
|
||||
: plan.PasswordManager.basePrice;
|
||||
} else if (!plan.PasswordManager.basePrice && plan.PasswordManager.hasAdditionalSeatsOption) {
|
||||
const secretsManagerCost = subscription.useSecretsManager
|
||||
? plan.SecretsManager.seatPrice
|
||||
: 0;
|
||||
const passwordManagerCost = isSecretsManagerTrial ? 0 : plan.PasswordManager.seatPrice;
|
||||
costPerMember = (secretsManagerCost + passwordManagerCost) / (plan.isAnnual ? 12 : 1);
|
||||
}
|
||||
|
||||
const percentOff = subscription.customerDiscount?.percentOff ?? 0;
|
||||
|
||||
const discount =
|
||||
(percentOff === 0 && plan.isAnnual) || isSecretsManagerTrial ? 20 : percentOff;
|
||||
|
||||
return {
|
||||
title: plan.isAnnual ? "Annually" : "Monthly",
|
||||
costPerMember,
|
||||
discount,
|
||||
isDisabled: false,
|
||||
isSelected: plan.isAnnual,
|
||||
isAnnual: plan.isAnnual,
|
||||
productTier: plan.productTier,
|
||||
};
|
||||
});
|
||||
|
||||
return planCards.reverse();
|
||||
}
|
||||
}
|
||||
155
apps/web/src/app/billing/services/pricing-summary.service.ts
Normal file
155
apps/web/src/app/billing/services/pricing-summary.service.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
|
||||
import { PlanInterval, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { TaxInformation } from "@bitwarden/common/billing/models/domain/tax-information";
|
||||
import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
|
||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||
|
||||
import { PricingSummaryData } from "../shared/pricing-summary/pricing-summary.component";
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
})
|
||||
export class PricingSummaryService {
|
||||
private estimatedTax: number = 0;
|
||||
|
||||
constructor(private taxService: TaxServiceAbstraction) {}
|
||||
|
||||
async getPricingSummaryData(
|
||||
plan: PlanResponse,
|
||||
sub: OrganizationSubscriptionResponse,
|
||||
organization: Organization,
|
||||
selectedInterval: PlanInterval,
|
||||
taxInformation: TaxInformation,
|
||||
isSecretsManagerTrial: boolean,
|
||||
): Promise<PricingSummaryData> {
|
||||
// Calculation helpers
|
||||
const passwordManagerSeatTotal =
|
||||
plan.PasswordManager?.hasAdditionalSeatsOption && !isSecretsManagerTrial
|
||||
? plan.PasswordManager.seatPrice * Math.abs(sub?.seats || 0)
|
||||
: 0;
|
||||
|
||||
const secretsManagerSeatTotal = plan.SecretsManager?.hasAdditionalSeatsOption
|
||||
? plan.SecretsManager.seatPrice * Math.abs(sub?.smSeats || 0)
|
||||
: 0;
|
||||
|
||||
const additionalServiceAccount = this.getAdditionalServiceAccount(plan, sub);
|
||||
|
||||
const additionalStorageTotal = plan.PasswordManager?.hasAdditionalStorageOption
|
||||
? plan.PasswordManager.additionalStoragePricePerGb *
|
||||
(sub?.maxStorageGb ? sub.maxStorageGb - 1 : 0)
|
||||
: 0;
|
||||
|
||||
const additionalStoragePriceMonthly = plan.PasswordManager?.additionalStoragePricePerGb || 0;
|
||||
|
||||
const additionalServiceAccountTotal =
|
||||
plan.SecretsManager?.hasAdditionalServiceAccountOption && additionalServiceAccount > 0
|
||||
? plan.SecretsManager.additionalPricePerServiceAccount * additionalServiceAccount
|
||||
: 0;
|
||||
|
||||
let passwordManagerSubtotal = plan.PasswordManager?.basePrice || 0;
|
||||
if (plan.PasswordManager?.hasAdditionalSeatsOption) {
|
||||
passwordManagerSubtotal += passwordManagerSeatTotal;
|
||||
}
|
||||
if (plan.PasswordManager?.hasPremiumAccessOption) {
|
||||
passwordManagerSubtotal += plan.PasswordManager.premiumAccessOptionPrice;
|
||||
}
|
||||
|
||||
const secretsManagerSubtotal = plan.SecretsManager
|
||||
? (plan.SecretsManager.basePrice || 0) +
|
||||
secretsManagerSeatTotal +
|
||||
additionalServiceAccountTotal
|
||||
: 0;
|
||||
|
||||
const totalAppliedDiscount = 0;
|
||||
const discountPercentageFromSub = isSecretsManagerTrial
|
||||
? 0
|
||||
: (sub?.customerDiscount?.percentOff ?? 0);
|
||||
const discountPercentage = 20;
|
||||
const acceptingSponsorship = false;
|
||||
const storageGb = sub?.maxStorageGb ? sub?.maxStorageGb - 1 : 0;
|
||||
|
||||
this.estimatedTax = await this.getEstimatedTax(organization, plan, sub, taxInformation);
|
||||
|
||||
const total = organization?.useSecretsManager
|
||||
? passwordManagerSubtotal +
|
||||
additionalStorageTotal +
|
||||
secretsManagerSubtotal +
|
||||
this.estimatedTax
|
||||
: passwordManagerSubtotal + additionalStorageTotal + this.estimatedTax;
|
||||
|
||||
return {
|
||||
selectedPlanInterval: selectedInterval === PlanInterval.Annually ? "year" : "month",
|
||||
passwordManagerSeats:
|
||||
plan.productTier === ProductTierType.Families ? plan.PasswordManager.baseSeats : sub?.seats,
|
||||
passwordManagerSeatTotal,
|
||||
secretsManagerSeatTotal,
|
||||
additionalStorageTotal,
|
||||
additionalStoragePriceMonthly,
|
||||
additionalServiceAccountTotal,
|
||||
totalAppliedDiscount,
|
||||
secretsManagerSubtotal,
|
||||
passwordManagerSubtotal,
|
||||
total,
|
||||
organization,
|
||||
sub,
|
||||
selectedPlan: plan,
|
||||
selectedInterval,
|
||||
discountPercentageFromSub,
|
||||
discountPercentage,
|
||||
acceptingSponsorship,
|
||||
additionalServiceAccount,
|
||||
storageGb,
|
||||
isSecretsManagerTrial,
|
||||
estimatedTax: this.estimatedTax,
|
||||
};
|
||||
}
|
||||
|
||||
async getEstimatedTax(
|
||||
organization: Organization,
|
||||
currentPlan: PlanResponse,
|
||||
sub: OrganizationSubscriptionResponse,
|
||||
taxInformation: TaxInformation,
|
||||
) {
|
||||
if (!taxInformation || !taxInformation.country || !taxInformation.postalCode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const request: PreviewOrganizationInvoiceRequest = {
|
||||
organizationId: organization.id,
|
||||
passwordManager: {
|
||||
additionalStorage: 0,
|
||||
plan: currentPlan?.type,
|
||||
seats: sub.seats,
|
||||
},
|
||||
taxInformation: {
|
||||
postalCode: taxInformation.postalCode,
|
||||
country: taxInformation.country,
|
||||
taxId: taxInformation.taxId,
|
||||
},
|
||||
};
|
||||
|
||||
if (organization.useSecretsManager) {
|
||||
request.secretsManager = {
|
||||
seats: sub.smSeats ?? 0,
|
||||
additionalMachineAccounts:
|
||||
(sub.smServiceAccounts ?? 0) - (sub.plan.SecretsManager?.baseServiceAccount ?? 0),
|
||||
};
|
||||
}
|
||||
const invoiceResponse = await this.taxService.previewOrganizationInvoice(request);
|
||||
return invoiceResponse.taxAmount;
|
||||
}
|
||||
|
||||
getAdditionalServiceAccount(plan: PlanResponse, sub: OrganizationSubscriptionResponse): number {
|
||||
if (!plan || !plan.SecretsManager) {
|
||||
return 0;
|
||||
}
|
||||
const baseServiceAccount = plan.SecretsManager?.baseServiceAccount || 0;
|
||||
const usedServiceAccounts = sub?.smServiceAccounts || 0;
|
||||
const additionalServiceAccounts = baseServiceAccount - usedServiceAccounts;
|
||||
return additionalServiceAccounts <= 0 ? Math.abs(additionalServiceAccounts) : 0;
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,13 @@ import { BillingHistoryComponent } from "./billing-history.component";
|
||||
import { OffboardingSurveyComponent } from "./offboarding-survey.component";
|
||||
import { PaymentComponent } from "./payment/payment.component";
|
||||
import { PaymentMethodComponent } from "./payment-method.component";
|
||||
import { PlanCardComponent } from "./plan-card/plan-card.component";
|
||||
import { PricingSummaryComponent } from "./pricing-summary/pricing-summary.component";
|
||||
import { IndividualSelfHostingLicenseUploaderComponent } from "./self-hosting-license-uploader/individual-self-hosting-license-uploader.component";
|
||||
import { OrganizationSelfHostingLicenseUploaderComponent } from "./self-hosting-license-uploader/organization-self-hosting-license-uploader.component";
|
||||
import { SecretsManagerSubscribeComponent } from "./sm-subscribe.component";
|
||||
import { TaxInfoComponent } from "./tax-info.component";
|
||||
import { TrialPaymentDialogComponent } from "./trial-payment-dialog/trial-payment-dialog.component";
|
||||
import { UpdateLicenseDialogComponent } from "./update-license-dialog.component";
|
||||
import { UpdateLicenseComponent } from "./update-license.component";
|
||||
import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-account.component";
|
||||
@@ -41,6 +44,9 @@ import { VerifyBankAccountComponent } from "./verify-bank-account/verify-bank-ac
|
||||
AdjustStorageDialogComponent,
|
||||
IndividualSelfHostingLicenseUploaderComponent,
|
||||
OrganizationSelfHostingLicenseUploaderComponent,
|
||||
TrialPaymentDialogComponent,
|
||||
PlanCardComponent,
|
||||
PricingSummaryComponent,
|
||||
],
|
||||
exports: [
|
||||
SharedModule,
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
@let isFocused = plan().isSelected;
|
||||
@let isRecommended = plan().isAnnual;
|
||||
|
||||
<bit-card
|
||||
class="tw-h-full"
|
||||
[ngClass]="getPlanCardContainerClasses()"
|
||||
(click)="cardClicked.emit()"
|
||||
[attr.tabindex]="!isFocused || plan().isDisabled ? '-1' : '0'"
|
||||
[attr.data-selected]="plan()?.isSelected"
|
||||
>
|
||||
<div class="tw-relative">
|
||||
@if (isRecommended) {
|
||||
<div
|
||||
class="tw-bg-secondary-100 tw-text-center !tw-border-0 tw-text-sm tw-font-bold tw-py-1"
|
||||
[ngClass]="{
|
||||
'tw-bg-primary-700 !tw-text-contrast': plan().isSelected,
|
||||
'tw-bg-secondary-100': !plan().isSelected,
|
||||
}"
|
||||
>
|
||||
{{ "recommended" | i18n }}
|
||||
</div>
|
||||
}
|
||||
<div
|
||||
class="tw-px-2 tw-pb-[4px]"
|
||||
[ngClass]="{
|
||||
'tw-py-1': !plan().isSelected,
|
||||
'tw-py-0': plan().isSelected,
|
||||
}"
|
||||
>
|
||||
<h3
|
||||
class="tw-text-[1.25rem] tw-mt-[6px] tw-font-bold tw-mb-0 tw-leading-[2rem] tw-flex tw-items-center"
|
||||
>
|
||||
<span class="tw-capitalize tw-whitespace-nowrap">{{ plan().title }}</span>
|
||||
<!-- Discount Badge -->
|
||||
<span class="tw-mr-1 tw-ml-2" *ngIf="isRecommended" bitBadge variant="success">
|
||||
{{ "upgradeDiscount" | i18n: plan().discount }}</span
|
||||
>
|
||||
</h3>
|
||||
<span>
|
||||
<b class="tw-text-lg tw-font-semibold">{{ plan().costPerMember | currency: "$" }} </b>
|
||||
<span class="tw-text-xs tw-px-0"> /{{ "monthPerMember" | i18n }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</bit-card>
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Component, input, output } from "@angular/core";
|
||||
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
|
||||
export interface PlanCard {
|
||||
title: string;
|
||||
costPerMember: number;
|
||||
discount?: number;
|
||||
isDisabled: boolean;
|
||||
isAnnual: boolean;
|
||||
isSelected: boolean;
|
||||
productTier: ProductTierType;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-plan-card",
|
||||
templateUrl: "./plan-card.component.html",
|
||||
standalone: false,
|
||||
})
|
||||
export class PlanCardComponent {
|
||||
plan = input.required<PlanCard>();
|
||||
productTiers = ProductTierType;
|
||||
|
||||
cardClicked = output();
|
||||
|
||||
getPlanCardContainerClasses(): string[] {
|
||||
const isSelected = this.plan().isSelected;
|
||||
const isDisabled = this.plan().isDisabled;
|
||||
if (isDisabled) {
|
||||
return [
|
||||
"tw-cursor-not-allowed",
|
||||
"tw-bg-secondary-100",
|
||||
"tw-font-normal",
|
||||
"tw-bg-blur",
|
||||
"tw-text-muted",
|
||||
"tw-block",
|
||||
"tw-rounded",
|
||||
];
|
||||
}
|
||||
|
||||
return isSelected
|
||||
? [
|
||||
"tw-cursor-pointer",
|
||||
"tw-block",
|
||||
"tw-rounded",
|
||||
"tw-border",
|
||||
"tw-border-solid",
|
||||
"tw-border-primary-600",
|
||||
"tw-border-2",
|
||||
"tw-rounded-lg",
|
||||
"hover:tw-border-primary-700",
|
||||
"focus:tw-border-3",
|
||||
"focus:tw-border-primary-700",
|
||||
"focus:tw-rounded-lg",
|
||||
]
|
||||
: [
|
||||
"tw-cursor-pointer",
|
||||
"tw-block",
|
||||
"tw-rounded",
|
||||
"tw-border",
|
||||
"tw-border-solid",
|
||||
"tw-border-secondary-300",
|
||||
"hover:tw-border-text-main",
|
||||
"focus:tw-border-2",
|
||||
"focus:tw-border-primary-700",
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<ng-container>
|
||||
<div class="tw-mt-4">
|
||||
<p class="tw-text-lg tw-mb-1">
|
||||
<span class="tw-font-semibold"
|
||||
>{{ "total" | i18n }}:
|
||||
{{ summaryData.total - summaryData.totalAppliedDiscount | currency: "USD" : "$" }} USD</span
|
||||
>
|
||||
<span class="tw-text-xs tw-font-light"> / {{ summaryData.selectedPlanInterval | i18n }}</span>
|
||||
<button
|
||||
(click)="toggleTotalOpened()"
|
||||
type="button"
|
||||
[bitIconButton]="summaryData.totalOpened ? 'bwi-angle-down' : 'bwi-angle-up'"
|
||||
size="small"
|
||||
aria-hidden="true"
|
||||
></button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="summaryData.totalOpened">
|
||||
<!-- Main content container -->
|
||||
<div class="tw-flex tw-flex-wrap tw-gap-4">
|
||||
<bit-hint class="tw-w-full">
|
||||
<ng-container *ngIf="summaryData.isSecretsManagerTrial; else showPasswordManagerFirst">
|
||||
<ng-container *ngTemplateOutlet="secretsManagerSection"></ng-container>
|
||||
<ng-container *ngTemplateOutlet="passwordManagerSection"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #showPasswordManagerFirst>
|
||||
<ng-container *ngTemplateOutlet="passwordManagerSection"></ng-container>
|
||||
<ng-container *ngTemplateOutlet="secretsManagerSection"></ng-container>
|
||||
</ng-template>
|
||||
|
||||
<!-- Password Manager section -->
|
||||
<ng-template #passwordManagerSection>
|
||||
<ng-container
|
||||
*ngIf="!summaryData.isSecretsManagerTrial || summaryData.organization.useSecretsManager"
|
||||
>
|
||||
<p class="tw-font-semibold tw-mt-3 tw-mb-1">{{ "passwordManager" | i18n }}</p>
|
||||
|
||||
<!-- Base Price -->
|
||||
<ng-container *ngIf="summaryData.selectedPlan.PasswordManager.basePrice">
|
||||
<p class="tw-mb-1 tw-flex tw-justify-between" bitTypography="body2">
|
||||
<span>
|
||||
<ng-container [ngSwitch]="summaryData.selectedInterval">
|
||||
<ng-container *ngSwitchCase="planIntervals.Annually">
|
||||
{{ summaryData.passwordManagerSeats }} {{ "members" | i18n }} ×
|
||||
{{
|
||||
(summaryData.selectedPlan.isAnnual
|
||||
? summaryData.selectedPlan.PasswordManager.basePrice / 12
|
||||
: summaryData.selectedPlan.PasswordManager.basePrice
|
||||
) | currency: "$"
|
||||
}}
|
||||
/{{ summaryData.selectedPlanInterval | i18n }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
{{ "basePrice" | i18n }}:
|
||||
{{ summaryData.selectedPlan.PasswordManager.basePrice | currency: "$" }}
|
||||
{{ "monthAbbr" | i18n }}
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</span>
|
||||
<span>
|
||||
<ng-container
|
||||
*ngIf="summaryData.acceptingSponsorship; else notAcceptingSponsorship"
|
||||
>
|
||||
<span class="tw-line-through">{{
|
||||
summaryData.selectedPlan.PasswordManager.basePrice | currency: "$"
|
||||
}}</span>
|
||||
{{ "freeWithSponsorship" | i18n }}
|
||||
</ng-container>
|
||||
<ng-template #notAcceptingSponsorship>
|
||||
{{ summaryData.selectedPlan.PasswordManager.basePrice | currency: "$" }}
|
||||
</ng-template>
|
||||
</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
|
||||
<!-- Additional Seats -->
|
||||
<ng-container *ngIf="summaryData.selectedPlan.PasswordManager.hasAdditionalSeatsOption">
|
||||
<p class="tw-mb-0 tw-flex tw-justify-between" bitTypography="body2">
|
||||
<span>
|
||||
<span *ngIf="summaryData.selectedPlan.PasswordManager.baseSeats"
|
||||
>{{ "additionalUsers" | i18n }}:</span
|
||||
>
|
||||
{{ summaryData.passwordManagerSeats || 0 }}
|
||||
<span *ngIf="!summaryData.selectedPlan.PasswordManager.baseSeats">{{
|
||||
"members" | i18n
|
||||
}}</span>
|
||||
×
|
||||
{{ summaryData.selectedPlan.PasswordManager.seatPrice | currency: "$" }}
|
||||
/{{ summaryData.selectedPlanInterval | i18n }}
|
||||
</span>
|
||||
<span *ngIf="!summaryData.isSecretsManagerTrial">
|
||||
{{ summaryData.passwordManagerSeatTotal | currency: "$" }}
|
||||
</span>
|
||||
<span *ngIf="summaryData.isSecretsManagerTrial">
|
||||
{{ "freeForOneYear" | i18n }}
|
||||
</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
|
||||
<!-- Additional Storage -->
|
||||
<ng-container
|
||||
*ngIf="
|
||||
summaryData.selectedPlan.PasswordManager.hasAdditionalStorageOption &&
|
||||
summaryData.storageGb > 0
|
||||
"
|
||||
>
|
||||
<p class="tw-mb-0 tw-flex tw-justify-between" bitTypography="body2">
|
||||
<span>
|
||||
{{ summaryData.storageGb }} {{ "additionalStorageGbMessage" | i18n }}
|
||||
×
|
||||
{{ summaryData.additionalStoragePriceMonthly | currency: "$" }}
|
||||
/{{ summaryData.selectedPlanInterval | i18n }}
|
||||
</span>
|
||||
<span>
|
||||
<ng-container [ngSwitch]="summaryData.selectedInterval">
|
||||
<ng-container *ngSwitchCase="planIntervals.Annually">
|
||||
{{ summaryData.additionalStorageTotal | currency: "$" }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
{{
|
||||
summaryData.storageGb *
|
||||
summaryData.selectedPlan.PasswordManager.additionalStoragePricePerGb
|
||||
| currency: "$"
|
||||
}}
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
<!-- Secrets Manager section -->
|
||||
<ng-template #secretsManagerSection>
|
||||
<ng-container *ngIf="summaryData.organization.useSecretsManager">
|
||||
<p class="tw-font-semibold tw-mt-3 tw-mb-1">{{ "secretsManager" | i18n }}</p>
|
||||
|
||||
<!-- Base Price -->
|
||||
<ng-container *ngIf="summaryData.selectedPlan?.SecretsManager?.basePrice">
|
||||
<p class="tw-mb-1 tw-flex tw-justify-between" bitTypography="body2">
|
||||
<span>
|
||||
<ng-container [ngSwitch]="summaryData.selectedInterval">
|
||||
<ng-container *ngSwitchCase="planIntervals.Annually">
|
||||
{{ summaryData.sub?.smSeats }} {{ "members" | i18n }} ×
|
||||
{{
|
||||
(summaryData.selectedPlan.isAnnual
|
||||
? summaryData.selectedPlan.SecretsManager.basePrice / 12
|
||||
: summaryData.selectedPlan.SecretsManager.basePrice
|
||||
) | currency: "$"
|
||||
}}
|
||||
/{{ summaryData.selectedPlanInterval | i18n }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
{{ "basePrice" | i18n }}:
|
||||
{{ summaryData.selectedPlan.SecretsManager.basePrice | currency: "$" }}
|
||||
{{ "monthAbbr" | i18n }}
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</span>
|
||||
<span *ngIf="summaryData.selectedInterval === planIntervals.Monthly">
|
||||
{{ summaryData.selectedPlan.SecretsManager.basePrice | currency: "$" }}
|
||||
</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
|
||||
<!-- Additional Seats -->
|
||||
<ng-container
|
||||
*ngIf="summaryData.selectedPlan?.SecretsManager?.hasAdditionalSeatsOption"
|
||||
>
|
||||
<p class="tw-mb-0 tw-flex tw-justify-between" bitTypography="body2">
|
||||
<span>
|
||||
<span *ngIf="summaryData.selectedPlan.SecretsManager.baseSeats"
|
||||
>{{ "additionalUsers" | i18n }}:</span
|
||||
>
|
||||
{{ summaryData.sub?.smSeats || 0 }}
|
||||
<span *ngIf="!summaryData.selectedPlan.SecretsManager.baseSeats">{{
|
||||
"members" | i18n
|
||||
}}</span>
|
||||
×
|
||||
{{ summaryData.selectedPlan.SecretsManager.seatPrice | currency: "$" }}
|
||||
/{{ summaryData.selectedPlanInterval | i18n }}
|
||||
</span>
|
||||
<span>
|
||||
{{ summaryData.secretsManagerSeatTotal | currency: "$" }}
|
||||
</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
|
||||
<!-- Additional Service Accounts -->
|
||||
<ng-container
|
||||
*ngIf="
|
||||
summaryData.selectedPlan?.SecretsManager?.hasAdditionalServiceAccountOption &&
|
||||
summaryData.additionalServiceAccount > 0
|
||||
"
|
||||
>
|
||||
<p class="tw-mb-0 tw-flex tw-justify-between" bitTypography="body2">
|
||||
<span>
|
||||
{{ summaryData.additionalServiceAccount }}
|
||||
{{ "serviceAccounts" | i18n | lowercase }}
|
||||
×
|
||||
{{
|
||||
summaryData.selectedPlan?.SecretsManager?.additionalPricePerServiceAccount
|
||||
| currency: "$"
|
||||
}}
|
||||
/{{ summaryData.selectedPlanInterval | i18n }}
|
||||
</span>
|
||||
<span>{{ summaryData.additionalServiceAccountTotal | currency: "$" }}</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
<!-- Discount Section -->
|
||||
<ng-container *ngIf="summaryData.discountPercentageFromSub > 0">
|
||||
<p class="tw-mb-0 tw-flex tw-justify-between" bitTypography="body2">
|
||||
<span class="tw-text-xs">
|
||||
{{
|
||||
"providerDiscount" | i18n: this.summaryData.discountPercentageFromSub | lowercase
|
||||
}}
|
||||
</span>
|
||||
<span class="tw-line-through tw-text-xs">
|
||||
{{ summaryData.totalAppliedDiscount | currency: "$" }}
|
||||
</span>
|
||||
</p>
|
||||
</ng-container>
|
||||
</bit-hint>
|
||||
</div>
|
||||
|
||||
<!-- Tax and Total Section -->
|
||||
<div class="tw-flex tw-flex-wrap tw-gap-4 tw-mt-4">
|
||||
<bit-hint class="tw-w-full">
|
||||
<p
|
||||
class="tw-flex tw-justify-between tw-border-0 tw-border-solid tw-border-t tw-border-secondary-300 tw-pt-2 tw-mb-0"
|
||||
>
|
||||
<span class="tw-font-semibold">{{ "estimatedTax" | i18n }}</span>
|
||||
<span>{{ summaryData.estimatedTax | currency: "USD" : "$" }}</span>
|
||||
</p>
|
||||
</bit-hint>
|
||||
</div>
|
||||
|
||||
<div class="tw-flex tw-flex-wrap tw-gap-4 tw-mt-4">
|
||||
<bit-hint class="tw-w-full">
|
||||
<p
|
||||
class="tw-flex tw-justify-between tw-border-0 tw-border-solid tw-border-t tw-border-secondary-300 tw-pt-2 tw-mb-0"
|
||||
>
|
||||
<span class="tw-font-semibold">{{ "total" | i18n }}</span>
|
||||
<span>
|
||||
{{ summaryData.total - summaryData.totalAppliedDiscount | currency: "USD" : "$" }}
|
||||
<span class="tw-text-xs tw-font-semibold"
|
||||
>/ {{ summaryData.selectedPlanInterval | i18n }}</span
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
</bit-hint>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { PlanInterval } from "@bitwarden/common/billing/enums";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
|
||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||
|
||||
export interface PricingSummaryData {
|
||||
selectedPlanInterval: string;
|
||||
passwordManagerSeats: number;
|
||||
passwordManagerSeatTotal: number;
|
||||
secretsManagerSeatTotal: number;
|
||||
additionalStorageTotal: number;
|
||||
additionalStoragePriceMonthly: number;
|
||||
additionalServiceAccountTotal: number;
|
||||
totalAppliedDiscount: number;
|
||||
secretsManagerSubtotal: number;
|
||||
passwordManagerSubtotal: number;
|
||||
total: number;
|
||||
organization?: Organization;
|
||||
sub?: OrganizationSubscriptionResponse;
|
||||
selectedPlan?: PlanResponse;
|
||||
selectedInterval?: PlanInterval;
|
||||
discountPercentageFromSub?: number;
|
||||
discountPercentage?: number;
|
||||
acceptingSponsorship?: boolean;
|
||||
additionalServiceAccount?: number;
|
||||
totalOpened?: boolean;
|
||||
storageGb?: number;
|
||||
isSecretsManagerTrial?: boolean;
|
||||
estimatedTax?: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-pricing-summary",
|
||||
templateUrl: "./pricing-summary.component.html",
|
||||
standalone: false,
|
||||
})
|
||||
export class PricingSummaryComponent {
|
||||
@Input() summaryData!: PricingSummaryData;
|
||||
planIntervals = PlanInterval;
|
||||
|
||||
toggleTotalOpened(): void {
|
||||
if (this.summaryData) {
|
||||
this.summaryData.totalOpened = !this.summaryData.totalOpened;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<bit-dialog dialogSize="default">
|
||||
<span bitDialogTitle class="tw-font-semibold">
|
||||
{{ "subscribetoEnterprise" | i18n: currentPlanName }}
|
||||
</span>
|
||||
|
||||
<div bitDialogContent>
|
||||
<p>{{ "subscribeEnterpriseSubtitle" | i18n: currentPlanName }}</p>
|
||||
|
||||
<!-- Plan Features List -->
|
||||
<ng-container [ngSwitch]="currentPlan?.productTier">
|
||||
<ul class="bwi-ul tw-text-xs" *ngSwitchCase="productTypes.Enterprise">
|
||||
<li>
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "includeEnterprisePolicies" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "passwordLessSso" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "accountRecovery" | i18n }}
|
||||
</li>
|
||||
<li *ngIf="!organization?.canAccessSecretsManager">
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "customRoles" | i18n }}
|
||||
</li>
|
||||
<li *ngIf="organization?.canAccessSecretsManager">
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "unlimitedSecretsAndProjects" | i18n }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="bwi-ul tw-text-xs" *ngSwitchCase="productTypes.Teams">
|
||||
<li>
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "secureDataSharing" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "eventLogMonitoring" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "directoryIntegration" | i18n }}
|
||||
</li>
|
||||
<li *ngIf="organization?.canAccessSecretsManager">
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "unlimitedSecretsAndProjects" | i18n }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="bwi-ul tw-text-xs" *ngSwitchCase="productTypes.Families">
|
||||
<li>
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "premiumAccounts" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "unlimitedSharing" | i18n }}
|
||||
</li>
|
||||
<li>
|
||||
<i class="bwi bwi-check tw-text-muted bwi-li" aria-hidden="true"></i>
|
||||
{{ "createUnlimitedCollections" | i18n }}
|
||||
</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
|
||||
<div *ngIf="!(currentPlan?.productTier === productTypes.Families)">
|
||||
<div class="tw-mb-3 tw-flex tw-justify-between">
|
||||
<h4 class="tw-text-lg tw-text-main">{{ "selectAPlan" | i18n }}</h4>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="planCards().length > 0">
|
||||
<div
|
||||
class="tw-grid tw-grid-flow-col tw-gap-4 tw-mb-2"
|
||||
[class]="'tw-grid-cols-' + planCards().length"
|
||||
>
|
||||
@for (planCard of planCards(); track $index) {
|
||||
<app-plan-card [plan]="planCard" (cardClicked)="setSelected(planCard)"></app-plan-card>
|
||||
}
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<!-- Payment Information -->
|
||||
<ng-container>
|
||||
<h2 bitTypography="h4">{{ "paymentMethod" | i18n }}</h2>
|
||||
<ng-container bitDialogContent>
|
||||
<app-payment
|
||||
[showAccountCredit]="false"
|
||||
[showBankAccount]="!!organizationId"
|
||||
[initialPaymentMethod]="initialPaymentMethod"
|
||||
></app-payment>
|
||||
<app-manage-tax-information
|
||||
*ngIf="taxInformation"
|
||||
[showTaxIdField]="showTaxIdField"
|
||||
[startWith]="taxInformation"
|
||||
(taxInformationChanged)="taxInformationChanged($event)"
|
||||
/>
|
||||
</ng-container>
|
||||
<!-- Pricing Breakdown -->
|
||||
<app-pricing-summary
|
||||
*ngIf="pricingSummaryData"
|
||||
[summaryData]="pricingSummaryData"
|
||||
></app-pricing-summary>
|
||||
</ng-container>
|
||||
</div>
|
||||
<!-- Dialog Footer -->
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton buttonType="primary" type="button" [bitAction]="onSubscribe.bind(this)">
|
||||
{{ "subscribe" | i18n }}
|
||||
</button>
|
||||
<button bitButton buttonType="secondary" type="button" [bitDialogClose]="ResultType.CLOSED">
|
||||
{{ "later" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
@@ -0,0 +1,365 @@
|
||||
import { Component, EventEmitter, Inject, OnInit, Output, signal, ViewChild } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import {
|
||||
getOrganizationById,
|
||||
OrganizationService,
|
||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
import { OrganizationBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-billing-api.service.abstraction";
|
||||
import { PaymentMethodType, PlanInterval, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { TaxInformation } from "@bitwarden/common/billing/models/domain";
|
||||
import { ChangePlanFrequencyRequest } from "@bitwarden/common/billing/models/request/change-plan-frequency.request";
|
||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||
import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request";
|
||||
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
|
||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import {
|
||||
DIALOG_DATA,
|
||||
DialogConfig,
|
||||
DialogRef,
|
||||
DialogService,
|
||||
ToastService,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
import { PlanCardService } from "../../services/plan-card.service";
|
||||
import { PaymentComponent } from "../payment/payment.component";
|
||||
import { PlanCard } from "../plan-card/plan-card.component";
|
||||
import { PricingSummaryData } from "../pricing-summary/pricing-summary.component";
|
||||
|
||||
import { PricingSummaryService } from "./../../services/pricing-summary.service";
|
||||
|
||||
type TrialPaymentDialogParams = {
|
||||
organizationId: string;
|
||||
subscription: OrganizationSubscriptionResponse;
|
||||
productTierType: ProductTierType;
|
||||
initialPaymentMethod?: PaymentMethodType;
|
||||
};
|
||||
|
||||
export const TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE = {
|
||||
CLOSED: "closed",
|
||||
SUBMITTED: "submitted",
|
||||
} as const;
|
||||
|
||||
export type TrialPaymentDialogResultType =
|
||||
(typeof TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE)[keyof typeof TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE];
|
||||
|
||||
interface OnSuccessArgs {
|
||||
organizationId: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-trial-payment-dialog",
|
||||
templateUrl: "./trial-payment-dialog.component.html",
|
||||
standalone: false,
|
||||
})
|
||||
export class TrialPaymentDialogComponent implements OnInit {
|
||||
@ViewChild(PaymentComponent) paymentComponent!: PaymentComponent;
|
||||
@ViewChild(ManageTaxInformationComponent) taxComponent!: ManageTaxInformationComponent;
|
||||
|
||||
currentPlan!: PlanResponse;
|
||||
currentPlanName!: string;
|
||||
productTypes = ProductTierType;
|
||||
organization!: Organization;
|
||||
organizationId!: string;
|
||||
sub!: OrganizationSubscriptionResponse;
|
||||
selectedInterval: PlanInterval = PlanInterval.Annually;
|
||||
|
||||
planCards = signal<PlanCard[]>([]);
|
||||
plans!: ListResponse<PlanResponse>;
|
||||
|
||||
@Output() onSuccess = new EventEmitter<OnSuccessArgs>();
|
||||
protected initialPaymentMethod: PaymentMethodType;
|
||||
protected taxInformation!: TaxInformation;
|
||||
protected readonly ResultType = TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE;
|
||||
pricingSummaryData!: PricingSummaryData;
|
||||
|
||||
constructor(
|
||||
@Inject(DIALOG_DATA) private dialogParams: TrialPaymentDialogParams,
|
||||
private dialogRef: DialogRef<TrialPaymentDialogResultType>,
|
||||
private organizationService: OrganizationService,
|
||||
private i18nService: I18nService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private accountService: AccountService,
|
||||
private planCardService: PlanCardService,
|
||||
private pricingSummaryService: PricingSummaryService,
|
||||
private apiService: ApiService,
|
||||
private toastService: ToastService,
|
||||
private billingApiService: BillingApiServiceAbstraction,
|
||||
private organizationBillingApiServiceAbstraction: OrganizationBillingApiServiceAbstraction,
|
||||
) {
|
||||
this.initialPaymentMethod = this.dialogParams.initialPaymentMethod ?? PaymentMethodType.Card;
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.currentPlanName = this.resolvePlanName(this.dialogParams.productTierType);
|
||||
this.sub =
|
||||
this.dialogParams.subscription ??
|
||||
(await this.organizationApiService.getSubscription(this.dialogParams.organizationId));
|
||||
this.organizationId = this.dialogParams.organizationId;
|
||||
this.currentPlan = this.sub?.plan;
|
||||
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)));
|
||||
if (!userId) {
|
||||
throw new Error("User ID is required");
|
||||
}
|
||||
const organization = await firstValueFrom(
|
||||
this.organizationService
|
||||
.organizations$(userId)
|
||||
.pipe(getOrganizationById(this.organizationId)),
|
||||
);
|
||||
if (!organization) {
|
||||
throw new Error("Organization not found");
|
||||
}
|
||||
this.organization = organization;
|
||||
|
||||
const planCards = await this.planCardService.getCadenceCards(
|
||||
this.currentPlan,
|
||||
this.sub,
|
||||
this.isSecretsManagerTrial(),
|
||||
);
|
||||
|
||||
this.planCards.set(planCards);
|
||||
|
||||
if (!this.selectedInterval) {
|
||||
this.selectedInterval = planCards.find((card) => card.isSelected)?.isAnnual
|
||||
? PlanInterval.Annually
|
||||
: PlanInterval.Monthly;
|
||||
}
|
||||
|
||||
const taxInfo = await this.organizationApiService.getTaxInfo(this.organizationId);
|
||||
this.taxInformation = TaxInformation.from(taxInfo);
|
||||
|
||||
this.pricingSummaryData = await this.pricingSummaryService.getPricingSummaryData(
|
||||
this.currentPlan,
|
||||
this.sub,
|
||||
this.organization,
|
||||
this.selectedInterval,
|
||||
this.taxInformation,
|
||||
this.isSecretsManagerTrial(),
|
||||
);
|
||||
|
||||
this.plans = await this.apiService.getPlans();
|
||||
}
|
||||
|
||||
static open = (
|
||||
dialogService: DialogService,
|
||||
dialogConfig: DialogConfig<TrialPaymentDialogParams>,
|
||||
) => dialogService.open<TrialPaymentDialogResultType>(TrialPaymentDialogComponent, dialogConfig);
|
||||
|
||||
async setSelected(planCard: PlanCard) {
|
||||
this.selectedInterval = planCard.isAnnual ? PlanInterval.Annually : PlanInterval.Monthly;
|
||||
|
||||
this.planCards.update((planCards) => {
|
||||
return planCards.map((planCard) => {
|
||||
if (planCard.isSelected) {
|
||||
return {
|
||||
...planCard,
|
||||
isSelected: false,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...planCard,
|
||||
isSelected: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await this.selectPlan();
|
||||
|
||||
this.pricingSummaryData = await this.pricingSummaryService.getPricingSummaryData(
|
||||
this.currentPlan,
|
||||
this.sub,
|
||||
this.organization,
|
||||
this.selectedInterval,
|
||||
this.taxInformation,
|
||||
this.isSecretsManagerTrial(),
|
||||
);
|
||||
}
|
||||
|
||||
protected async selectPlan() {
|
||||
if (
|
||||
this.selectedInterval === PlanInterval.Monthly &&
|
||||
this.currentPlan.productTier == ProductTierType.Families
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filteredPlans = this.plans.data.filter(
|
||||
(plan) =>
|
||||
plan.productTier === this.currentPlan.productTier &&
|
||||
plan.isAnnual === (this.selectedInterval === PlanInterval.Annually),
|
||||
);
|
||||
if (filteredPlans.length > 0) {
|
||||
this.currentPlan = filteredPlans[0];
|
||||
}
|
||||
try {
|
||||
await this.refreshSalesTax();
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
const translatedMessage = this.i18nService.t(errorMessage);
|
||||
this.toastService.showToast({
|
||||
title: "",
|
||||
variant: "error",
|
||||
message: !translatedMessage || translatedMessage === "" ? errorMessage : translatedMessage,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected get showTaxIdField(): boolean {
|
||||
switch (this.currentPlan.productTier) {
|
||||
case ProductTierType.Free:
|
||||
case ProductTierType.Families:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private async refreshSalesTax(): Promise<void> {
|
||||
if (
|
||||
this.taxInformation === undefined ||
|
||||
!this.taxInformation.country ||
|
||||
!this.taxInformation.postalCode
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const request: PreviewOrganizationInvoiceRequest = {
|
||||
organizationId: this.organizationId,
|
||||
passwordManager: {
|
||||
additionalStorage: 0,
|
||||
plan: this.currentPlan?.type,
|
||||
seats: this.sub.seats,
|
||||
},
|
||||
taxInformation: {
|
||||
postalCode: this.taxInformation.postalCode,
|
||||
country: this.taxInformation.country,
|
||||
taxId: this.taxInformation.taxId,
|
||||
},
|
||||
};
|
||||
|
||||
if (this.organization.useSecretsManager) {
|
||||
request.secretsManager = {
|
||||
seats: this.sub.smSeats ?? 0,
|
||||
additionalMachineAccounts:
|
||||
(this.sub.smServiceAccounts ?? 0) -
|
||||
(this.sub.plan.SecretsManager?.baseServiceAccount ?? 0),
|
||||
};
|
||||
}
|
||||
|
||||
this.pricingSummaryData = await this.pricingSummaryService.getPricingSummaryData(
|
||||
this.currentPlan,
|
||||
this.sub,
|
||||
this.organization,
|
||||
this.selectedInterval,
|
||||
this.taxInformation,
|
||||
this.isSecretsManagerTrial(),
|
||||
);
|
||||
}
|
||||
|
||||
async taxInformationChanged(event: TaxInformation) {
|
||||
this.taxInformation = event;
|
||||
this.toggleBankAccount();
|
||||
await this.refreshSalesTax();
|
||||
}
|
||||
|
||||
toggleBankAccount = () => {
|
||||
this.paymentComponent.showBankAccount = this.taxInformation.country === "US";
|
||||
|
||||
if (
|
||||
!this.paymentComponent.showBankAccount &&
|
||||
this.paymentComponent.selected === PaymentMethodType.BankAccount
|
||||
) {
|
||||
this.paymentComponent.select(PaymentMethodType.Card);
|
||||
}
|
||||
};
|
||||
|
||||
isSecretsManagerTrial(): boolean {
|
||||
return (
|
||||
this.sub?.subscription?.items?.some((item) =>
|
||||
this.sub?.customerDiscount?.appliesTo?.includes(item.productId),
|
||||
) ?? false
|
||||
);
|
||||
}
|
||||
|
||||
async onSubscribe(): Promise<void> {
|
||||
if (!this.taxComponent.validate()) {
|
||||
this.taxComponent.markAllAsTouched();
|
||||
}
|
||||
try {
|
||||
await this.updateOrganizationPaymentMethod(
|
||||
this.organizationId,
|
||||
this.paymentComponent,
|
||||
this.taxInformation,
|
||||
);
|
||||
|
||||
if (this.currentPlan.type !== this.sub.planType) {
|
||||
const changePlanRequest = new ChangePlanFrequencyRequest();
|
||||
changePlanRequest.newPlanType = this.currentPlan.type;
|
||||
await this.organizationBillingApiServiceAbstraction.changeSubscriptionFrequency(
|
||||
this.organizationId,
|
||||
changePlanRequest,
|
||||
);
|
||||
}
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: undefined,
|
||||
message: this.i18nService.t("updatedPaymentMethod"),
|
||||
});
|
||||
|
||||
this.onSuccess.emit({ organizationId: this.organizationId });
|
||||
this.dialogRef.close(TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE.SUBMITTED);
|
||||
} catch (error) {
|
||||
const msg =
|
||||
typeof error === "object" && error !== null && "message" in error
|
||||
? (error as { message: string }).message
|
||||
: String(error);
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: undefined,
|
||||
message: this.i18nService.t(msg) || msg,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async updateOrganizationPaymentMethod(
|
||||
organizationId: string,
|
||||
paymentComponent: PaymentComponent,
|
||||
taxInformation: TaxInformation,
|
||||
): Promise<void> {
|
||||
const paymentSource = await paymentComponent.tokenize();
|
||||
|
||||
const request = new UpdatePaymentMethodRequest();
|
||||
request.paymentSource = paymentSource;
|
||||
request.taxInformation = ExpandedTaxInfoUpdateRequest.From(taxInformation);
|
||||
|
||||
await this.billingApiService.updateOrganizationPaymentMethod(organizationId, request);
|
||||
}
|
||||
|
||||
resolvePlanName(productTier: ProductTierType): string {
|
||||
switch (productTier) {
|
||||
case ProductTierType.Enterprise:
|
||||
return this.i18nService.t("planNameEnterprise");
|
||||
case ProductTierType.Free:
|
||||
return this.i18nService.t("planNameFree");
|
||||
case ProductTierType.Families:
|
||||
return this.i18nService.t("planNameFamilies");
|
||||
case ProductTierType.Teams:
|
||||
return this.i18nService.t("planNameTeams");
|
||||
case ProductTierType.TeamsStarter:
|
||||
return this.i18nService.t("planNameTeamsStarter");
|
||||
default:
|
||||
return this.i18nService.t("planNameFree");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { AsyncPipe } from "@angular/common";
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import { Observable } from "rxjs";
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { takeUntil } from "rxjs/operators";
|
||||
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { AnchorLinkDirective, BannerComponent } from "@bitwarden/components";
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
|
||||
@@ -37,16 +39,28 @@ import { OrganizationFreeTrialWarning } from "../types";
|
||||
`,
|
||||
imports: [AnchorLinkDirective, AsyncPipe, BannerComponent, I18nPipe],
|
||||
})
|
||||
export class OrganizationFreeTrialWarningComponent implements OnInit {
|
||||
export class OrganizationFreeTrialWarningComponent implements OnInit, OnDestroy {
|
||||
@Input({ required: true }) organization!: Organization;
|
||||
@Output() clicked = new EventEmitter<void>();
|
||||
|
||||
warning$!: Observable<OrganizationFreeTrialWarning>;
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(private organizationWarningsService: OrganizationWarningsService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.warning$ = this.organizationWarningsService.getFreeTrialWarning$(this.organization);
|
||||
this.organizationWarningsService
|
||||
.refreshWarningsForOrganization$(this.organization.id as OrganizationId)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => {
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
refresh = () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Location } from "@angular/common";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { filter, from, lastValueFrom, map, Observable, switchMap, takeWhile } from "rxjs";
|
||||
import { filter, from, lastValueFrom, map, Observable, Subject, switchMap, takeWhile } from "rxjs";
|
||||
import { take } from "rxjs/operators";
|
||||
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
@@ -10,10 +11,15 @@ import { OrganizationWarningsResponse } from "@bitwarden/common/billing/models/r
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { openChangePlanDialog } from "../../organizations/change-plan-dialog.component";
|
||||
import {
|
||||
TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE,
|
||||
TrialPaymentDialogComponent,
|
||||
} from "../../shared/trial-payment-dialog/trial-payment-dialog.component";
|
||||
import { OrganizationFreeTrialWarning, OrganizationResellerRenewalWarning } from "../types";
|
||||
|
||||
const format = (date: Date) =>
|
||||
@@ -26,6 +32,7 @@ const format = (date: Date) =>
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class OrganizationWarningsService {
|
||||
private cache$ = new Map<OrganizationId, Observable<OrganizationWarningsResponse>>();
|
||||
private refreshWarnings$ = new Subject<OrganizationId>();
|
||||
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
@@ -34,6 +41,8 @@ export class OrganizationWarningsService {
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private organizationBillingApiService: OrganizationBillingApiServiceAbstraction,
|
||||
private router: Router,
|
||||
private location: Location,
|
||||
protected syncService: SyncService,
|
||||
) {}
|
||||
|
||||
getFreeTrialWarning$ = (
|
||||
@@ -174,10 +183,33 @@ export class OrganizationWarningsService {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "add_payment_method_optional_trial": {
|
||||
const organizationSubscriptionResponse =
|
||||
await this.organizationApiService.getSubscription(organization.id);
|
||||
|
||||
const dialogRef = TrialPaymentDialogComponent.open(this.dialogService, {
|
||||
data: {
|
||||
organizationId: organization.id,
|
||||
subscription: organizationSubscriptionResponse,
|
||||
productTierType: organization?.productTierType,
|
||||
},
|
||||
});
|
||||
const result = await lastValueFrom(dialogRef.closed);
|
||||
if (result === TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE.SUBMITTED) {
|
||||
this.refreshWarnings$.next(organization.id as OrganizationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
refreshWarningsForOrganization$(organizationId: OrganizationId): Observable<void> {
|
||||
return this.refreshWarnings$.pipe(
|
||||
filter((id) => id === organizationId),
|
||||
map((): void => void 0),
|
||||
);
|
||||
}
|
||||
|
||||
private getResponse$ = (
|
||||
organization: Organization,
|
||||
bypassCache: boolean = false,
|
||||
|
||||
@@ -342,9 +342,9 @@ export class EventService {
|
||||
);
|
||||
break;
|
||||
case EventType.OrganizationUser_Deleted:
|
||||
msg = this.i18nService.t("deletedUserId", this.formatOrgUserId(ev));
|
||||
msg = this.i18nService.t("deletedUserIdEventMessage", this.formatOrgUserId(ev));
|
||||
humanReadableMsg = this.i18nService.t(
|
||||
"deletedUserId",
|
||||
"deletedUserIdEventMessage",
|
||||
this.getShortId(ev.organizationUserId),
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -568,6 +568,9 @@
|
||||
"cancel": {
|
||||
"message": "Cancel"
|
||||
},
|
||||
"later": {
|
||||
"message": "Later"
|
||||
},
|
||||
"canceled": {
|
||||
"message": "Canceled"
|
||||
},
|
||||
@@ -4630,6 +4633,9 @@
|
||||
"receiveMarketingEmailsV2": {
|
||||
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
|
||||
},
|
||||
"subscribe": {
|
||||
"message": "Subscribe"
|
||||
},
|
||||
"unsubscribe": {
|
||||
"message": "Unsubscribe"
|
||||
},
|
||||
@@ -10289,8 +10295,8 @@
|
||||
"organizationUserDeletedDesc": {
|
||||
"message": "The user was removed from the organization and all associated user data has been deleted."
|
||||
},
|
||||
"deletedUserId": {
|
||||
"message": "Deleted user $ID$ - an owner / admin deleted the user account",
|
||||
"deletedUserIdEventMessage": {
|
||||
"message": "Deleted user $ID$",
|
||||
"placeholders": {
|
||||
"id": {
|
||||
"content": "$1",
|
||||
@@ -10900,5 +10906,26 @@
|
||||
"example": "12-3456789"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subscribetoEnterprise": {
|
||||
"message": "Subscribe to $PLAN$",
|
||||
"placeholders": {
|
||||
"plan": {
|
||||
"content": "$1",
|
||||
"example": "Teams"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subscribeEnterpriseSubtitle": {
|
||||
"message": "Your 7-day $PLAN$ trial starts today. Add a payment method now to continue using these features after your trial ends: ",
|
||||
"placeholders": {
|
||||
"plan": {
|
||||
"content": "$1",
|
||||
"example": "Teams"
|
||||
}
|
||||
}
|
||||
},
|
||||
"unlimitedSecretsAndProjects": {
|
||||
"message": "Unlimited secrets and projects"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
|
||||
@@ -249,7 +249,7 @@ export class LoginDecryptionOptionsComponent implements OnInit {
|
||||
}
|
||||
|
||||
try {
|
||||
const { publicKey, privateKey } = await this.keyService.initAccount();
|
||||
const { publicKey, privateKey } = await this.keyService.initAccount(this.activeAccountId);
|
||||
const keysRequest = new KeysRequest(publicKey, privateKey.encryptedString);
|
||||
await this.apiService.postAccountKeys(keysRequest);
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { MasterKey } from "@bitwarden/common/types/key";
|
||||
import { KdfConfig } from "@bitwarden/key-management";
|
||||
@@ -31,5 +29,5 @@ export abstract class SetPasswordJitService {
|
||||
* @throws If any property on the `credentials` object is null or undefined, or if a protectedUserKey
|
||||
* or newKeyPair could not be created.
|
||||
*/
|
||||
setPassword: (credentials: SetPasswordCredentials) => Promise<void>;
|
||||
abstract setPassword(credentials: SetPasswordCredentials): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable";
|
||||
@@ -10,20 +8,20 @@ import { UserKey, MasterKey } from "@bitwarden/common/types/key";
|
||||
|
||||
export abstract class AuthRequestServiceAbstraction {
|
||||
/** Emits an auth request id when an auth request has been approved. */
|
||||
authRequestPushNotification$: Observable<string>;
|
||||
abstract authRequestPushNotification$: Observable<string>;
|
||||
|
||||
/**
|
||||
* Emits when a login has been approved by an admin. This emission is specifically for the
|
||||
* purpose of notifying the consuming component to display a toast informing the user.
|
||||
*/
|
||||
adminLoginApproved$: Observable<void>;
|
||||
abstract adminLoginApproved$: Observable<void>;
|
||||
|
||||
/**
|
||||
* Returns an admin auth request for the given user if it exists.
|
||||
* @param userId The user id.
|
||||
* @throws If `userId` is not provided.
|
||||
*/
|
||||
abstract getAdminAuthRequest: (userId: UserId) => Promise<AdminAuthRequestStorable | null>;
|
||||
abstract getAdminAuthRequest(userId: UserId): Promise<AdminAuthRequestStorable | null>;
|
||||
/**
|
||||
* Sets an admin auth request for the given user.
|
||||
* Note: use {@link clearAdminAuthRequest} to clear the request.
|
||||
@@ -31,16 +29,16 @@ export abstract class AuthRequestServiceAbstraction {
|
||||
* @param userId The user id.
|
||||
* @throws If `authRequest` or `userId` is not provided.
|
||||
*/
|
||||
abstract setAdminAuthRequest: (
|
||||
abstract setAdminAuthRequest(
|
||||
authRequest: AdminAuthRequestStorable,
|
||||
userId: UserId,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
/**
|
||||
* Clears an admin auth request for the given user.
|
||||
* @param userId The user id.
|
||||
* @throws If `userId` is not provided.
|
||||
*/
|
||||
abstract clearAdminAuthRequest: (userId: UserId) => Promise<void>;
|
||||
abstract clearAdminAuthRequest(userId: UserId): Promise<void>;
|
||||
/**
|
||||
* Gets a list of standard pending auth requests for the user.
|
||||
* @returns An observable of an array of auth request.
|
||||
@@ -61,42 +59,42 @@ export abstract class AuthRequestServiceAbstraction {
|
||||
* approval was successful.
|
||||
* @throws If the auth request is missing an id or key.
|
||||
*/
|
||||
abstract approveOrDenyAuthRequest: (
|
||||
abstract approveOrDenyAuthRequest(
|
||||
approve: boolean,
|
||||
authRequest: AuthRequestResponse,
|
||||
) => Promise<AuthRequestResponse>;
|
||||
): Promise<AuthRequestResponse>;
|
||||
/**
|
||||
* Sets the `UserKey` from an auth request. Auth request must have a `UserKey`.
|
||||
* @param authReqResponse The auth request.
|
||||
* @param authReqPrivateKey The private key corresponding to the public key sent in the auth request.
|
||||
* @param userId The ID of the user for whose account we will set the key.
|
||||
*/
|
||||
abstract setUserKeyAfterDecryptingSharedUserKey: (
|
||||
abstract setUserKeyAfterDecryptingSharedUserKey(
|
||||
authReqResponse: AuthRequestResponse,
|
||||
authReqPrivateKey: ArrayBuffer,
|
||||
userId: UserId,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
/**
|
||||
* Sets the `MasterKey` and `MasterKeyHash` from an auth request. Auth request must have a `MasterKey` and `MasterKeyHash`.
|
||||
* @param authReqResponse The auth request.
|
||||
* @param authReqPrivateKey The private key corresponding to the public key sent in the auth request.
|
||||
* @param userId The ID of the user for whose account we will set the keys.
|
||||
*/
|
||||
abstract setKeysAfterDecryptingSharedMasterKeyAndHash: (
|
||||
abstract setKeysAfterDecryptingSharedMasterKeyAndHash(
|
||||
authReqResponse: AuthRequestResponse,
|
||||
authReqPrivateKey: ArrayBuffer,
|
||||
userId: UserId,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
/**
|
||||
* Decrypts a `UserKey` from a public key encrypted `UserKey`.
|
||||
* @param pubKeyEncryptedUserKey The public key encrypted `UserKey`.
|
||||
* @param privateKey The private key corresponding to the public key used to encrypt the `UserKey`.
|
||||
* @returns The decrypted `UserKey`.
|
||||
*/
|
||||
abstract decryptPubKeyEncryptedUserKey: (
|
||||
abstract decryptPubKeyEncryptedUserKey(
|
||||
pubKeyEncryptedUserKey: string,
|
||||
privateKey: ArrayBuffer,
|
||||
) => Promise<UserKey>;
|
||||
): Promise<UserKey>;
|
||||
/**
|
||||
* Decrypts a `MasterKey` and `MasterKeyHash` from a public key encrypted `MasterKey` and `MasterKeyHash`.
|
||||
* @param pubKeyEncryptedMasterKey The public key encrypted `MasterKey`.
|
||||
@@ -104,18 +102,18 @@ export abstract class AuthRequestServiceAbstraction {
|
||||
* @param privateKey The private key corresponding to the public key used to encrypt the `MasterKey` and `MasterKeyHash`.
|
||||
* @returns The decrypted `MasterKey` and `MasterKeyHash`.
|
||||
*/
|
||||
abstract decryptPubKeyEncryptedMasterKeyAndHash: (
|
||||
abstract decryptPubKeyEncryptedMasterKeyAndHash(
|
||||
pubKeyEncryptedMasterKey: string,
|
||||
pubKeyEncryptedMasterKeyHash: string,
|
||||
privateKey: ArrayBuffer,
|
||||
) => Promise<{ masterKey: MasterKey; masterKeyHash: string }>;
|
||||
): Promise<{ masterKey: MasterKey; masterKeyHash: string }>;
|
||||
|
||||
/**
|
||||
* Handles incoming auth request push notifications.
|
||||
* @param notification push notification.
|
||||
* @remark We should only be receiving approved push notifications to prevent enumeration.
|
||||
*/
|
||||
abstract sendAuthRequestPushNotification: (notification: AuthRequestPushNotification) => void;
|
||||
abstract sendAuthRequestPushNotification(notification: AuthRequestPushNotification): void;
|
||||
|
||||
/**
|
||||
* Creates a dash-delimited fingerprint for use in confirming the `AuthRequest` between the requesting and approving device.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type";
|
||||
@@ -20,60 +18,60 @@ export abstract class LoginStrategyServiceAbstraction {
|
||||
* The current strategy being used to authenticate.
|
||||
* Emits null if the session has timed out.
|
||||
*/
|
||||
currentAuthType$: Observable<AuthenticationType | null>;
|
||||
abstract currentAuthType$: Observable<AuthenticationType | null>;
|
||||
/**
|
||||
* If the login strategy uses the email address of the user, this
|
||||
* will return it. Otherwise, it will return null.
|
||||
*/
|
||||
getEmail: () => Promise<string | null>;
|
||||
abstract getEmail(): Promise<string | null>;
|
||||
/**
|
||||
* If the user is logging in with a master password, this will return
|
||||
* the master password hash. Otherwise, it will return null.
|
||||
*/
|
||||
getMasterPasswordHash: () => Promise<string | null>;
|
||||
abstract getMasterPasswordHash(): Promise<string | null>;
|
||||
/**
|
||||
* If the user is logging in with SSO, this will return
|
||||
* the email auth token. Otherwise, it will return null.
|
||||
* @see {@link SsoLoginStrategyData.ssoEmail2FaSessionToken}
|
||||
*/
|
||||
getSsoEmail2FaSessionToken: () => Promise<string | null>;
|
||||
abstract getSsoEmail2FaSessionToken(): Promise<string | null>;
|
||||
/**
|
||||
* Returns the access code if the user is logging in with an
|
||||
* Auth Request. Otherwise, it will return null.
|
||||
*/
|
||||
getAccessCode: () => Promise<string | null>;
|
||||
abstract getAccessCode(): Promise<string | null>;
|
||||
/**
|
||||
* Returns the auth request ID if the user is logging in with an
|
||||
* Auth Request. Otherwise, it will return null.
|
||||
*/
|
||||
getAuthRequestId: () => Promise<string | null>;
|
||||
abstract getAuthRequestId(): Promise<string | null>;
|
||||
/**
|
||||
* Sends a token request to the server using the provided credentials.
|
||||
*/
|
||||
logIn: (
|
||||
abstract logIn(
|
||||
credentials:
|
||||
| UserApiLoginCredentials
|
||||
| PasswordLoginCredentials
|
||||
| SsoLoginCredentials
|
||||
| AuthRequestLoginCredentials
|
||||
| WebAuthnLoginCredentials,
|
||||
) => Promise<AuthResult>;
|
||||
): Promise<AuthResult>;
|
||||
/**
|
||||
* Sends a token request to the server with the provided two factor token.
|
||||
* This uses data stored from {@link LoginStrategyServiceAbstraction.logIn}, so that must be called first.
|
||||
* Returns an error if no session data is found.
|
||||
*/
|
||||
logInTwoFactor: (twoFactor: TokenTwoFactorRequest) => Promise<AuthResult>;
|
||||
abstract logInTwoFactor(twoFactor: TokenTwoFactorRequest): Promise<AuthResult>;
|
||||
/**
|
||||
* Creates a master key from the provided master password and email.
|
||||
*/
|
||||
makePreloginKey: (masterPassword: string, email: string) => Promise<MasterKey>;
|
||||
abstract makePreloginKey(masterPassword: string, email: string): Promise<MasterKey>;
|
||||
/**
|
||||
* Emits true if the authentication session has expired.
|
||||
*/
|
||||
authenticationSessionTimeout$: Observable<boolean>;
|
||||
abstract get authenticationSessionTimeout$(): Observable<boolean>;
|
||||
/**
|
||||
* Sends a token request to the server with the provided device verification OTP.
|
||||
*/
|
||||
logInNewDeviceVerification: (deviceVerificationOtp: string) => Promise<AuthResult>;
|
||||
abstract logInNewDeviceVerification(deviceVerificationOtp: string): Promise<AuthResult>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import {
|
||||
@@ -128,7 +126,7 @@ import { OptionalCipherResponse } from "../vault/models/response/optional-cipher
|
||||
* of this decision please read https://contributing.bitwarden.com/architecture/adr/refactor-api-service.
|
||||
*/
|
||||
export abstract class ApiService {
|
||||
send: (
|
||||
abstract send(
|
||||
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||
path: string,
|
||||
body: any,
|
||||
@@ -136,196 +134,225 @@ export abstract class ApiService {
|
||||
hasResponse: boolean,
|
||||
apiUrl?: string | null,
|
||||
alterHeaders?: (headers: Headers) => void,
|
||||
) => Promise<any>;
|
||||
): Promise<any>;
|
||||
|
||||
postIdentityToken: (
|
||||
abstract postIdentityToken(
|
||||
request:
|
||||
| PasswordTokenRequest
|
||||
| SsoTokenRequest
|
||||
| UserApiTokenRequest
|
||||
| WebAuthnLoginTokenRequest,
|
||||
) => Promise<
|
||||
): Promise<
|
||||
IdentityTokenResponse | IdentityTwoFactorResponse | IdentityDeviceVerificationResponse
|
||||
>;
|
||||
refreshIdentityToken: () => Promise<any>;
|
||||
abstract refreshIdentityToken(): Promise<any>;
|
||||
|
||||
getProfile: () => Promise<ProfileResponse>;
|
||||
getUserSubscription: () => Promise<SubscriptionResponse>;
|
||||
getTaxInfo: () => Promise<TaxInfoResponse>;
|
||||
putProfile: (request: UpdateProfileRequest) => Promise<ProfileResponse>;
|
||||
putAvatar: (request: UpdateAvatarRequest) => Promise<ProfileResponse>;
|
||||
putTaxInfo: (request: TaxInfoUpdateRequest) => Promise<any>;
|
||||
postPrelogin: (request: PreloginRequest) => Promise<PreloginResponse>;
|
||||
postEmailToken: (request: EmailTokenRequest) => Promise<any>;
|
||||
postEmail: (request: EmailRequest) => Promise<any>;
|
||||
postSetKeyConnectorKey: (request: SetKeyConnectorKeyRequest) => Promise<any>;
|
||||
postSecurityStamp: (request: SecretVerificationRequest) => Promise<any>;
|
||||
getAccountRevisionDate: () => Promise<number>;
|
||||
postPasswordHint: (request: PasswordHintRequest) => Promise<any>;
|
||||
postPremium: (data: FormData) => Promise<PaymentResponse>;
|
||||
postReinstatePremium: () => Promise<any>;
|
||||
postAccountStorage: (request: StorageRequest) => Promise<PaymentResponse>;
|
||||
postAccountPayment: (request: PaymentRequest) => Promise<void>;
|
||||
postAccountLicense: (data: FormData) => Promise<any>;
|
||||
postAccountKeys: (request: KeysRequest) => Promise<any>;
|
||||
postAccountVerifyEmail: () => Promise<any>;
|
||||
postAccountVerifyEmailToken: (request: VerifyEmailRequest) => Promise<any>;
|
||||
postAccountRecoverDelete: (request: DeleteRecoverRequest) => Promise<any>;
|
||||
postAccountRecoverDeleteToken: (request: VerifyDeleteRecoverRequest) => Promise<any>;
|
||||
postAccountKdf: (request: KdfRequest) => Promise<any>;
|
||||
postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
postConvertToKeyConnector: () => Promise<void>;
|
||||
abstract getProfile(): Promise<ProfileResponse>;
|
||||
abstract getUserSubscription(): Promise<SubscriptionResponse>;
|
||||
abstract getTaxInfo(): Promise<TaxInfoResponse>;
|
||||
abstract putProfile(request: UpdateProfileRequest): Promise<ProfileResponse>;
|
||||
abstract putAvatar(request: UpdateAvatarRequest): Promise<ProfileResponse>;
|
||||
abstract putTaxInfo(request: TaxInfoUpdateRequest): Promise<any>;
|
||||
abstract postPrelogin(request: PreloginRequest): Promise<PreloginResponse>;
|
||||
abstract postEmailToken(request: EmailTokenRequest): Promise<any>;
|
||||
abstract postEmail(request: EmailRequest): Promise<any>;
|
||||
abstract postSetKeyConnectorKey(request: SetKeyConnectorKeyRequest): Promise<any>;
|
||||
abstract postSecurityStamp(request: SecretVerificationRequest): Promise<any>;
|
||||
abstract getAccountRevisionDate(): Promise<number>;
|
||||
abstract postPasswordHint(request: PasswordHintRequest): Promise<any>;
|
||||
abstract postPremium(data: FormData): Promise<PaymentResponse>;
|
||||
abstract postReinstatePremium(): Promise<any>;
|
||||
abstract postAccountStorage(request: StorageRequest): Promise<PaymentResponse>;
|
||||
abstract postAccountPayment(request: PaymentRequest): Promise<void>;
|
||||
abstract postAccountLicense(data: FormData): Promise<any>;
|
||||
abstract postAccountKeys(request: KeysRequest): Promise<any>;
|
||||
abstract postAccountVerifyEmail(): Promise<any>;
|
||||
abstract postAccountVerifyEmailToken(request: VerifyEmailRequest): Promise<any>;
|
||||
abstract postAccountRecoverDelete(request: DeleteRecoverRequest): Promise<any>;
|
||||
abstract postAccountRecoverDeleteToken(request: VerifyDeleteRecoverRequest): Promise<any>;
|
||||
abstract postAccountKdf(request: KdfRequest): Promise<any>;
|
||||
abstract postUserApiKey(id: string, request: SecretVerificationRequest): Promise<ApiKeyResponse>;
|
||||
abstract postUserRotateApiKey(
|
||||
id: string,
|
||||
request: SecretVerificationRequest,
|
||||
): Promise<ApiKeyResponse>;
|
||||
abstract postConvertToKeyConnector(): Promise<void>;
|
||||
//passwordless
|
||||
getAuthRequest: (id: string) => Promise<AuthRequestResponse>;
|
||||
putAuthRequest: (id: string, request: PasswordlessAuthRequest) => Promise<AuthRequestResponse>;
|
||||
getAuthRequests: () => Promise<ListResponse<AuthRequestResponse>>;
|
||||
getLastAuthRequest: () => Promise<AuthRequestResponse>;
|
||||
abstract getAuthRequest(id: string): Promise<AuthRequestResponse>;
|
||||
abstract putAuthRequest(
|
||||
id: string,
|
||||
request: PasswordlessAuthRequest,
|
||||
): Promise<AuthRequestResponse>;
|
||||
abstract getAuthRequests(): Promise<ListResponse<AuthRequestResponse>>;
|
||||
abstract getLastAuthRequest(): Promise<AuthRequestResponse>;
|
||||
|
||||
getUserBillingHistory: () => Promise<BillingHistoryResponse>;
|
||||
getUserBillingPayment: () => Promise<BillingPaymentResponse>;
|
||||
abstract getUserBillingHistory(): Promise<BillingHistoryResponse>;
|
||||
abstract getUserBillingPayment(): Promise<BillingPaymentResponse>;
|
||||
|
||||
getCipher: (id: string) => Promise<CipherResponse>;
|
||||
getFullCipherDetails: (id: string) => Promise<CipherResponse>;
|
||||
getCipherAdmin: (id: string) => Promise<CipherResponse>;
|
||||
getAttachmentData: (
|
||||
abstract getCipher(id: string): Promise<CipherResponse>;
|
||||
abstract getFullCipherDetails(id: string): Promise<CipherResponse>;
|
||||
abstract getCipherAdmin(id: string): Promise<CipherResponse>;
|
||||
abstract getAttachmentData(
|
||||
cipherId: string,
|
||||
attachmentId: string,
|
||||
emergencyAccessId?: string,
|
||||
) => Promise<AttachmentResponse>;
|
||||
getAttachmentDataAdmin: (cipherId: string, attachmentId: string) => Promise<AttachmentResponse>;
|
||||
getCiphersOrganization: (organizationId: string) => Promise<ListResponse<CipherResponse>>;
|
||||
postCipher: (request: CipherRequest) => Promise<CipherResponse>;
|
||||
postCipherCreate: (request: CipherCreateRequest) => Promise<CipherResponse>;
|
||||
postCipherAdmin: (request: CipherCreateRequest) => Promise<CipherResponse>;
|
||||
putCipher: (id: string, request: CipherRequest) => Promise<CipherResponse>;
|
||||
putPartialCipher: (id: string, request: CipherPartialRequest) => Promise<CipherResponse>;
|
||||
putCipherAdmin: (id: string, request: CipherRequest) => Promise<CipherResponse>;
|
||||
deleteCipher: (id: string) => Promise<any>;
|
||||
deleteCipherAdmin: (id: string) => Promise<any>;
|
||||
deleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise<any>;
|
||||
deleteManyCiphersAdmin: (request: CipherBulkDeleteRequest) => Promise<any>;
|
||||
putMoveCiphers: (request: CipherBulkMoveRequest) => Promise<any>;
|
||||
putShareCipher: (id: string, request: CipherShareRequest) => Promise<CipherResponse>;
|
||||
putShareCiphers: (request: CipherBulkShareRequest) => Promise<ListResponse<CipherResponse>>;
|
||||
putCipherCollections: (
|
||||
): Promise<AttachmentResponse>;
|
||||
abstract getAttachmentDataAdmin(
|
||||
cipherId: string,
|
||||
attachmentId: string,
|
||||
): Promise<AttachmentResponse>;
|
||||
abstract getCiphersOrganization(organizationId: string): Promise<ListResponse<CipherResponse>>;
|
||||
abstract postCipher(request: CipherRequest): Promise<CipherResponse>;
|
||||
abstract postCipherCreate(request: CipherCreateRequest): Promise<CipherResponse>;
|
||||
abstract postCipherAdmin(request: CipherCreateRequest): Promise<CipherResponse>;
|
||||
abstract putCipher(id: string, request: CipherRequest): Promise<CipherResponse>;
|
||||
abstract putPartialCipher(id: string, request: CipherPartialRequest): Promise<CipherResponse>;
|
||||
abstract putCipherAdmin(id: string, request: CipherRequest): Promise<CipherResponse>;
|
||||
abstract deleteCipher(id: string): Promise<any>;
|
||||
abstract deleteCipherAdmin(id: string): Promise<any>;
|
||||
abstract deleteManyCiphers(request: CipherBulkDeleteRequest): Promise<any>;
|
||||
abstract deleteManyCiphersAdmin(request: CipherBulkDeleteRequest): Promise<any>;
|
||||
abstract putMoveCiphers(request: CipherBulkMoveRequest): Promise<any>;
|
||||
abstract putShareCipher(id: string, request: CipherShareRequest): Promise<CipherResponse>;
|
||||
abstract putShareCiphers(request: CipherBulkShareRequest): Promise<ListResponse<CipherResponse>>;
|
||||
abstract putCipherCollections(
|
||||
id: string,
|
||||
request: CipherCollectionsRequest,
|
||||
) => Promise<OptionalCipherResponse>;
|
||||
putCipherCollectionsAdmin: (id: string, request: CipherCollectionsRequest) => Promise<any>;
|
||||
postPurgeCiphers: (request: SecretVerificationRequest, organizationId?: string) => Promise<any>;
|
||||
putDeleteCipher: (id: string) => Promise<any>;
|
||||
putDeleteCipherAdmin: (id: string) => Promise<any>;
|
||||
putDeleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise<any>;
|
||||
putDeleteManyCiphersAdmin: (request: CipherBulkDeleteRequest) => Promise<any>;
|
||||
putRestoreCipher: (id: string) => Promise<CipherResponse>;
|
||||
putRestoreCipherAdmin: (id: string) => Promise<CipherResponse>;
|
||||
putRestoreManyCiphers: (
|
||||
): Promise<OptionalCipherResponse>;
|
||||
abstract putCipherCollectionsAdmin(id: string, request: CipherCollectionsRequest): Promise<any>;
|
||||
abstract postPurgeCiphers(
|
||||
request: SecretVerificationRequest,
|
||||
organizationId?: string,
|
||||
): Promise<any>;
|
||||
abstract putDeleteCipher(id: string): Promise<any>;
|
||||
abstract putDeleteCipherAdmin(id: string): Promise<any>;
|
||||
abstract putDeleteManyCiphers(request: CipherBulkDeleteRequest): Promise<any>;
|
||||
abstract putDeleteManyCiphersAdmin(request: CipherBulkDeleteRequest): Promise<any>;
|
||||
abstract putRestoreCipher(id: string): Promise<CipherResponse>;
|
||||
abstract putRestoreCipherAdmin(id: string): Promise<CipherResponse>;
|
||||
abstract putRestoreManyCiphers(
|
||||
request: CipherBulkRestoreRequest,
|
||||
) => Promise<ListResponse<CipherResponse>>;
|
||||
putRestoreManyCiphersAdmin: (
|
||||
): Promise<ListResponse<CipherResponse>>;
|
||||
abstract putRestoreManyCiphersAdmin(
|
||||
request: CipherBulkRestoreRequest,
|
||||
) => Promise<ListResponse<CipherResponse>>;
|
||||
): Promise<ListResponse<CipherResponse>>;
|
||||
|
||||
postCipherAttachment: (
|
||||
abstract postCipherAttachment(
|
||||
id: string,
|
||||
request: AttachmentRequest,
|
||||
) => Promise<AttachmentUploadDataResponse>;
|
||||
deleteCipherAttachment: (id: string, attachmentId: string) => Promise<any>;
|
||||
deleteCipherAttachmentAdmin: (id: string, attachmentId: string) => Promise<any>;
|
||||
postShareCipherAttachment: (
|
||||
): Promise<AttachmentUploadDataResponse>;
|
||||
abstract deleteCipherAttachment(id: string, attachmentId: string): Promise<any>;
|
||||
abstract deleteCipherAttachmentAdmin(id: string, attachmentId: string): Promise<any>;
|
||||
abstract postShareCipherAttachment(
|
||||
id: string,
|
||||
attachmentId: string,
|
||||
data: FormData,
|
||||
organizationId: string,
|
||||
) => Promise<any>;
|
||||
renewAttachmentUploadUrl: (
|
||||
): Promise<any>;
|
||||
abstract renewAttachmentUploadUrl(
|
||||
id: string,
|
||||
attachmentId: string,
|
||||
) => Promise<AttachmentUploadDataResponse>;
|
||||
postAttachmentFile: (id: string, attachmentId: string, data: FormData) => Promise<any>;
|
||||
): Promise<AttachmentUploadDataResponse>;
|
||||
abstract postAttachmentFile(id: string, attachmentId: string, data: FormData): Promise<any>;
|
||||
|
||||
getUserCollections: () => Promise<ListResponse<CollectionResponse>>;
|
||||
getCollections: (organizationId: string) => Promise<ListResponse<CollectionResponse>>;
|
||||
getCollectionUsers: (organizationId: string, id: string) => Promise<SelectionReadOnlyResponse[]>;
|
||||
getCollectionAccessDetails: (
|
||||
abstract getUserCollections(): Promise<ListResponse<CollectionResponse>>;
|
||||
abstract getCollections(organizationId: string): Promise<ListResponse<CollectionResponse>>;
|
||||
abstract getCollectionUsers(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
) => Promise<CollectionAccessDetailsResponse>;
|
||||
getManyCollectionsWithAccessDetails: (
|
||||
): Promise<SelectionReadOnlyResponse[]>;
|
||||
abstract getCollectionAccessDetails(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
): Promise<CollectionAccessDetailsResponse>;
|
||||
abstract getManyCollectionsWithAccessDetails(
|
||||
orgId: string,
|
||||
) => Promise<ListResponse<CollectionAccessDetailsResponse>>;
|
||||
postCollection: (
|
||||
): Promise<ListResponse<CollectionAccessDetailsResponse>>;
|
||||
abstract postCollection(
|
||||
organizationId: string,
|
||||
request: CollectionRequest,
|
||||
) => Promise<CollectionDetailsResponse>;
|
||||
putCollection: (
|
||||
): Promise<CollectionDetailsResponse>;
|
||||
abstract putCollection(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: CollectionRequest,
|
||||
) => Promise<CollectionDetailsResponse>;
|
||||
deleteCollection: (organizationId: string, id: string) => Promise<any>;
|
||||
deleteManyCollections: (organizationId: string, collectionIds: string[]) => Promise<any>;
|
||||
): Promise<CollectionDetailsResponse>;
|
||||
abstract deleteCollection(organizationId: string, id: string): Promise<any>;
|
||||
abstract deleteManyCollections(organizationId: string, collectionIds: string[]): Promise<any>;
|
||||
|
||||
getGroupUsers: (organizationId: string, id: string) => Promise<string[]>;
|
||||
deleteGroupUser: (organizationId: string, id: string, organizationUserId: string) => Promise<any>;
|
||||
|
||||
getSync: () => Promise<SyncResponse>;
|
||||
|
||||
getSettingsDomains: () => Promise<DomainsResponse>;
|
||||
putSettingsDomains: (request: UpdateDomainsRequest) => Promise<DomainsResponse>;
|
||||
|
||||
getTwoFactorProviders: () => Promise<ListResponse<TwoFactorProviderResponse>>;
|
||||
getTwoFactorOrganizationProviders: (
|
||||
abstract getGroupUsers(organizationId: string, id: string): Promise<string[]>;
|
||||
abstract deleteGroupUser(
|
||||
organizationId: string,
|
||||
) => Promise<ListResponse<TwoFactorProviderResponse>>;
|
||||
getTwoFactorAuthenticator: (
|
||||
id: string,
|
||||
organizationUserId: string,
|
||||
): Promise<any>;
|
||||
|
||||
abstract getSync(): Promise<SyncResponse>;
|
||||
|
||||
abstract getSettingsDomains(): Promise<DomainsResponse>;
|
||||
abstract putSettingsDomains(request: UpdateDomainsRequest): Promise<DomainsResponse>;
|
||||
|
||||
abstract getTwoFactorProviders(): Promise<ListResponse<TwoFactorProviderResponse>>;
|
||||
abstract getTwoFactorOrganizationProviders(
|
||||
organizationId: string,
|
||||
): Promise<ListResponse<TwoFactorProviderResponse>>;
|
||||
abstract getTwoFactorAuthenticator(
|
||||
request: SecretVerificationRequest,
|
||||
) => Promise<TwoFactorAuthenticatorResponse>;
|
||||
getTwoFactorEmail: (request: SecretVerificationRequest) => Promise<TwoFactorEmailResponse>;
|
||||
getTwoFactorDuo: (request: SecretVerificationRequest) => Promise<TwoFactorDuoResponse>;
|
||||
getTwoFactorOrganizationDuo: (
|
||||
): Promise<TwoFactorAuthenticatorResponse>;
|
||||
abstract getTwoFactorEmail(request: SecretVerificationRequest): Promise<TwoFactorEmailResponse>;
|
||||
abstract getTwoFactorDuo(request: SecretVerificationRequest): Promise<TwoFactorDuoResponse>;
|
||||
abstract getTwoFactorOrganizationDuo(
|
||||
organizationId: string,
|
||||
request: SecretVerificationRequest,
|
||||
) => Promise<TwoFactorDuoResponse>;
|
||||
getTwoFactorYubiKey: (request: SecretVerificationRequest) => Promise<TwoFactorYubiKeyResponse>;
|
||||
getTwoFactorWebAuthn: (request: SecretVerificationRequest) => Promise<TwoFactorWebAuthnResponse>;
|
||||
getTwoFactorWebAuthnChallenge: (request: SecretVerificationRequest) => Promise<ChallengeResponse>;
|
||||
getTwoFactorRecover: (request: SecretVerificationRequest) => Promise<TwoFactorRecoverResponse>;
|
||||
putTwoFactorAuthenticator: (
|
||||
): Promise<TwoFactorDuoResponse>;
|
||||
abstract getTwoFactorYubiKey(
|
||||
request: SecretVerificationRequest,
|
||||
): Promise<TwoFactorYubiKeyResponse>;
|
||||
abstract getTwoFactorWebAuthn(
|
||||
request: SecretVerificationRequest,
|
||||
): Promise<TwoFactorWebAuthnResponse>;
|
||||
abstract getTwoFactorWebAuthnChallenge(
|
||||
request: SecretVerificationRequest,
|
||||
): Promise<ChallengeResponse>;
|
||||
abstract getTwoFactorRecover(
|
||||
request: SecretVerificationRequest,
|
||||
): Promise<TwoFactorRecoverResponse>;
|
||||
abstract putTwoFactorAuthenticator(
|
||||
request: UpdateTwoFactorAuthenticatorRequest,
|
||||
) => Promise<TwoFactorAuthenticatorResponse>;
|
||||
deleteTwoFactorAuthenticator: (
|
||||
): Promise<TwoFactorAuthenticatorResponse>;
|
||||
abstract deleteTwoFactorAuthenticator(
|
||||
request: DisableTwoFactorAuthenticatorRequest,
|
||||
) => Promise<TwoFactorProviderResponse>;
|
||||
putTwoFactorEmail: (request: UpdateTwoFactorEmailRequest) => Promise<TwoFactorEmailResponse>;
|
||||
putTwoFactorDuo: (request: UpdateTwoFactorDuoRequest) => Promise<TwoFactorDuoResponse>;
|
||||
putTwoFactorOrganizationDuo: (
|
||||
): Promise<TwoFactorProviderResponse>;
|
||||
abstract putTwoFactorEmail(request: UpdateTwoFactorEmailRequest): Promise<TwoFactorEmailResponse>;
|
||||
abstract putTwoFactorDuo(request: UpdateTwoFactorDuoRequest): Promise<TwoFactorDuoResponse>;
|
||||
abstract putTwoFactorOrganizationDuo(
|
||||
organizationId: string,
|
||||
request: UpdateTwoFactorDuoRequest,
|
||||
) => Promise<TwoFactorDuoResponse>;
|
||||
putTwoFactorYubiKey: (
|
||||
): Promise<TwoFactorDuoResponse>;
|
||||
abstract putTwoFactorYubiKey(
|
||||
request: UpdateTwoFactorYubikeyOtpRequest,
|
||||
) => Promise<TwoFactorYubiKeyResponse>;
|
||||
putTwoFactorWebAuthn: (
|
||||
): Promise<TwoFactorYubiKeyResponse>;
|
||||
abstract putTwoFactorWebAuthn(
|
||||
request: UpdateTwoFactorWebAuthnRequest,
|
||||
) => Promise<TwoFactorWebAuthnResponse>;
|
||||
deleteTwoFactorWebAuthn: (
|
||||
): Promise<TwoFactorWebAuthnResponse>;
|
||||
abstract deleteTwoFactorWebAuthn(
|
||||
request: UpdateTwoFactorWebAuthnDeleteRequest,
|
||||
) => Promise<TwoFactorWebAuthnResponse>;
|
||||
putTwoFactorDisable: (request: TwoFactorProviderRequest) => Promise<TwoFactorProviderResponse>;
|
||||
putTwoFactorOrganizationDisable: (
|
||||
): Promise<TwoFactorWebAuthnResponse>;
|
||||
abstract putTwoFactorDisable(
|
||||
request: TwoFactorProviderRequest,
|
||||
): Promise<TwoFactorProviderResponse>;
|
||||
abstract putTwoFactorOrganizationDisable(
|
||||
organizationId: string,
|
||||
request: TwoFactorProviderRequest,
|
||||
) => Promise<TwoFactorProviderResponse>;
|
||||
postTwoFactorEmailSetup: (request: TwoFactorEmailRequest) => Promise<any>;
|
||||
postTwoFactorEmail: (request: TwoFactorEmailRequest) => Promise<any>;
|
||||
getDeviceVerificationSettings: () => Promise<DeviceVerificationResponse>;
|
||||
putDeviceVerificationSettings: (
|
||||
): Promise<TwoFactorProviderResponse>;
|
||||
abstract postTwoFactorEmailSetup(request: TwoFactorEmailRequest): Promise<any>;
|
||||
abstract postTwoFactorEmail(request: TwoFactorEmailRequest): Promise<any>;
|
||||
abstract getDeviceVerificationSettings(): Promise<DeviceVerificationResponse>;
|
||||
abstract putDeviceVerificationSettings(
|
||||
request: DeviceVerificationRequest,
|
||||
) => Promise<DeviceVerificationResponse>;
|
||||
): Promise<DeviceVerificationResponse>;
|
||||
|
||||
getCloudCommunicationsEnabled: () => Promise<boolean>;
|
||||
abstract getCloudCommunicationsEnabled(): Promise<boolean>;
|
||||
abstract getOrganizationConnection<TConfig extends OrganizationConnectionConfigApis>(
|
||||
id: string,
|
||||
type: OrganizationConnectionType,
|
||||
@@ -340,136 +367,147 @@ export abstract class ApiService {
|
||||
configType: { new (response: any): TConfig },
|
||||
organizationConnectionId: string,
|
||||
): Promise<OrganizationConnectionResponse<TConfig>>;
|
||||
deleteOrganizationConnection: (id: string) => Promise<void>;
|
||||
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
||||
abstract deleteOrganizationConnection(id: string): Promise<void>;
|
||||
abstract getPlans(): Promise<ListResponse<PlanResponse>>;
|
||||
|
||||
getProviderUsers: (providerId: string) => Promise<ListResponse<ProviderUserUserDetailsResponse>>;
|
||||
getProviderUser: (providerId: string, id: string) => Promise<ProviderUserResponse>;
|
||||
postProviderUserInvite: (providerId: string, request: ProviderUserInviteRequest) => Promise<any>;
|
||||
postProviderUserReinvite: (providerId: string, id: string) => Promise<any>;
|
||||
postManyProviderUserReinvite: (
|
||||
abstract getProviderUsers(
|
||||
providerId: string,
|
||||
): Promise<ListResponse<ProviderUserUserDetailsResponse>>;
|
||||
abstract getProviderUser(providerId: string, id: string): Promise<ProviderUserResponse>;
|
||||
abstract postProviderUserInvite(
|
||||
providerId: string,
|
||||
request: ProviderUserInviteRequest,
|
||||
): Promise<any>;
|
||||
abstract postProviderUserReinvite(providerId: string, id: string): Promise<any>;
|
||||
abstract postManyProviderUserReinvite(
|
||||
providerId: string,
|
||||
request: ProviderUserBulkRequest,
|
||||
) => Promise<ListResponse<ProviderUserBulkResponse>>;
|
||||
postProviderUserAccept: (
|
||||
): Promise<ListResponse<ProviderUserBulkResponse>>;
|
||||
abstract postProviderUserAccept(
|
||||
providerId: string,
|
||||
id: string,
|
||||
request: ProviderUserAcceptRequest,
|
||||
) => Promise<any>;
|
||||
postProviderUserConfirm: (
|
||||
): Promise<any>;
|
||||
abstract postProviderUserConfirm(
|
||||
providerId: string,
|
||||
id: string,
|
||||
request: ProviderUserConfirmRequest,
|
||||
) => Promise<any>;
|
||||
postProviderUsersPublicKey: (
|
||||
): Promise<any>;
|
||||
abstract postProviderUsersPublicKey(
|
||||
providerId: string,
|
||||
request: ProviderUserBulkRequest,
|
||||
) => Promise<ListResponse<ProviderUserBulkPublicKeyResponse>>;
|
||||
postProviderUserBulkConfirm: (
|
||||
): Promise<ListResponse<ProviderUserBulkPublicKeyResponse>>;
|
||||
abstract postProviderUserBulkConfirm(
|
||||
providerId: string,
|
||||
request: ProviderUserBulkConfirmRequest,
|
||||
) => Promise<ListResponse<ProviderUserBulkResponse>>;
|
||||
putProviderUser: (
|
||||
): Promise<ListResponse<ProviderUserBulkResponse>>;
|
||||
abstract putProviderUser(
|
||||
providerId: string,
|
||||
id: string,
|
||||
request: ProviderUserUpdateRequest,
|
||||
) => Promise<any>;
|
||||
deleteProviderUser: (organizationId: string, id: string) => Promise<any>;
|
||||
deleteManyProviderUsers: (
|
||||
): Promise<any>;
|
||||
abstract deleteProviderUser(organizationId: string, id: string): Promise<any>;
|
||||
abstract deleteManyProviderUsers(
|
||||
providerId: string,
|
||||
request: ProviderUserBulkRequest,
|
||||
) => Promise<ListResponse<ProviderUserBulkResponse>>;
|
||||
getProviderClients: (
|
||||
): Promise<ListResponse<ProviderUserBulkResponse>>;
|
||||
abstract getProviderClients(
|
||||
providerId: string,
|
||||
) => Promise<ListResponse<ProviderOrganizationOrganizationDetailsResponse>>;
|
||||
postProviderAddOrganization: (
|
||||
): Promise<ListResponse<ProviderOrganizationOrganizationDetailsResponse>>;
|
||||
abstract postProviderAddOrganization(
|
||||
providerId: string,
|
||||
request: ProviderAddOrganizationRequest,
|
||||
) => Promise<any>;
|
||||
postProviderCreateOrganization: (
|
||||
): Promise<any>;
|
||||
abstract postProviderCreateOrganization(
|
||||
providerId: string,
|
||||
request: ProviderOrganizationCreateRequest,
|
||||
) => Promise<ProviderOrganizationResponse>;
|
||||
deleteProviderOrganization: (providerId: string, organizationId: string) => Promise<any>;
|
||||
): Promise<ProviderOrganizationResponse>;
|
||||
abstract deleteProviderOrganization(providerId: string, organizationId: string): Promise<any>;
|
||||
|
||||
getEvents: (start: string, end: string, token: string) => Promise<ListResponse<EventResponse>>;
|
||||
getEventsCipher: (
|
||||
abstract getEvents(
|
||||
start: string,
|
||||
end: string,
|
||||
token: string,
|
||||
): Promise<ListResponse<EventResponse>>;
|
||||
abstract getEventsCipher(
|
||||
id: string,
|
||||
start: string,
|
||||
end: string,
|
||||
token: string,
|
||||
) => Promise<ListResponse<EventResponse>>;
|
||||
getEventsOrganization: (
|
||||
): Promise<ListResponse<EventResponse>>;
|
||||
abstract getEventsOrganization(
|
||||
id: string,
|
||||
start: string,
|
||||
end: string,
|
||||
token: string,
|
||||
) => Promise<ListResponse<EventResponse>>;
|
||||
getEventsOrganizationUser: (
|
||||
): Promise<ListResponse<EventResponse>>;
|
||||
abstract getEventsOrganizationUser(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
start: string,
|
||||
end: string,
|
||||
token: string,
|
||||
) => Promise<ListResponse<EventResponse>>;
|
||||
getEventsProvider: (
|
||||
): Promise<ListResponse<EventResponse>>;
|
||||
abstract getEventsProvider(
|
||||
id: string,
|
||||
start: string,
|
||||
end: string,
|
||||
token: string,
|
||||
) => Promise<ListResponse<EventResponse>>;
|
||||
getEventsProviderUser: (
|
||||
): Promise<ListResponse<EventResponse>>;
|
||||
abstract getEventsProviderUser(
|
||||
providerId: string,
|
||||
id: string,
|
||||
start: string,
|
||||
end: string,
|
||||
token: string,
|
||||
) => Promise<ListResponse<EventResponse>>;
|
||||
): Promise<ListResponse<EventResponse>>;
|
||||
|
||||
/**
|
||||
* Posts events for a user
|
||||
* @param request The array of events to upload
|
||||
* @param userId The optional user id the events belong to. If no user id is provided the active user id is used.
|
||||
*/
|
||||
postEventsCollect: (request: EventRequest[], userId?: UserId) => Promise<any>;
|
||||
abstract postEventsCollect(request: EventRequest[], userId?: UserId): Promise<any>;
|
||||
|
||||
deleteSsoUser: (organizationId: string) => Promise<void>;
|
||||
getSsoUserIdentifier: () => Promise<string>;
|
||||
abstract deleteSsoUser(organizationId: string): Promise<void>;
|
||||
abstract getSsoUserIdentifier(): Promise<string>;
|
||||
|
||||
getUserPublicKey: (id: string) => Promise<UserKeyResponse>;
|
||||
abstract getUserPublicKey(id: string): Promise<UserKeyResponse>;
|
||||
|
||||
getHibpBreach: (username: string) => Promise<BreachAccountResponse[]>;
|
||||
abstract getHibpBreach(username: string): Promise<BreachAccountResponse[]>;
|
||||
|
||||
postBitPayInvoice: (request: BitPayInvoiceRequest) => Promise<string>;
|
||||
postSetupPayment: () => Promise<string>;
|
||||
abstract postBitPayInvoice(request: BitPayInvoiceRequest): Promise<string>;
|
||||
abstract postSetupPayment(): Promise<string>;
|
||||
|
||||
getActiveBearerToken: () => Promise<string>;
|
||||
fetch: (request: Request) => Promise<Response>;
|
||||
nativeFetch: (request: Request) => Promise<Response>;
|
||||
abstract getActiveBearerToken(): Promise<string>;
|
||||
abstract fetch(request: Request): Promise<Response>;
|
||||
abstract nativeFetch(request: Request): Promise<Response>;
|
||||
|
||||
preValidateSso: (identifier: string) => Promise<SsoPreValidateResponse>;
|
||||
abstract preValidateSso(identifier: string): Promise<SsoPreValidateResponse>;
|
||||
|
||||
postCreateSponsorship: (
|
||||
abstract postCreateSponsorship(
|
||||
sponsorshipOrgId: string,
|
||||
request: OrganizationSponsorshipCreateRequest,
|
||||
) => Promise<void>;
|
||||
getSponsorshipSyncStatus: (
|
||||
): Promise<void>;
|
||||
abstract getSponsorshipSyncStatus(
|
||||
sponsoredOrgId: string,
|
||||
) => Promise<OrganizationSponsorshipSyncStatusResponse>;
|
||||
deleteRemoveSponsorship: (sponsoringOrgId: string) => Promise<void>;
|
||||
postPreValidateSponsorshipToken: (
|
||||
): Promise<OrganizationSponsorshipSyncStatusResponse>;
|
||||
abstract deleteRemoveSponsorship(sponsoringOrgId: string): Promise<void>;
|
||||
abstract postPreValidateSponsorshipToken(
|
||||
sponsorshipToken: string,
|
||||
) => Promise<PreValidateSponsorshipResponse>;
|
||||
postRedeemSponsorship: (
|
||||
): Promise<PreValidateSponsorshipResponse>;
|
||||
abstract postRedeemSponsorship(
|
||||
sponsorshipToken: string,
|
||||
request: OrganizationSponsorshipRedeemRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
getMasterKeyFromKeyConnector: (keyConnectorUrl: string) => Promise<KeyConnectorUserKeyResponse>;
|
||||
postUserKeyToKeyConnector: (
|
||||
abstract getMasterKeyFromKeyConnector(
|
||||
keyConnectorUrl: string,
|
||||
): Promise<KeyConnectorUserKeyResponse>;
|
||||
abstract postUserKeyToKeyConnector(
|
||||
keyConnectorUrl: string,
|
||||
request: KeyConnectorUserKeyRequest,
|
||||
) => Promise<void>;
|
||||
getKeyConnectorAlive: (keyConnectorUrl: string) => Promise<void>;
|
||||
getOrganizationExport: (organizationId: string) => Promise<OrganizationExportResponse>;
|
||||
): Promise<void>;
|
||||
abstract getKeyConnectorAlive(keyConnectorUrl: string): Promise<void>;
|
||||
abstract getOrganizationExport(organizationId: string): Promise<OrganizationExportResponse>;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { EventType } from "../../enums";
|
||||
import { CipherView } from "../../vault/models/view/cipher.view";
|
||||
|
||||
export abstract class EventCollectionService {
|
||||
collectMany: (
|
||||
abstract collectMany(
|
||||
eventType: EventType,
|
||||
ciphers: CipherView[],
|
||||
uploadImmediately?: boolean,
|
||||
) => Promise<any>;
|
||||
collect: (
|
||||
): Promise<any>;
|
||||
abstract collect(
|
||||
eventType: EventType,
|
||||
cipherId?: string,
|
||||
uploadImmediately?: boolean,
|
||||
organizationId?: string,
|
||||
) => Promise<any>;
|
||||
): Promise<any>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { UserId } from "../../types/guid";
|
||||
|
||||
export abstract class EventUploadService {
|
||||
uploadEvents: (userId?: UserId) => Promise<void>;
|
||||
abstract uploadEvents(userId?: UserId): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { ListResponse } from "../../../models/response/list.response";
|
||||
import { OrganizationDomainRequest } from "../../services/organization-domain/requests/organization-domain.request";
|
||||
|
||||
@@ -8,19 +6,19 @@ import { OrganizationDomainResponse } from "./responses/organization-domain.resp
|
||||
import { VerifiedOrganizationDomainSsoDetailsResponse } from "./responses/verified-organization-domain-sso-details.response";
|
||||
|
||||
export abstract class OrgDomainApiServiceAbstraction {
|
||||
getAllByOrgId: (orgId: string) => Promise<Array<OrganizationDomainResponse>>;
|
||||
getByOrgIdAndOrgDomainId: (
|
||||
abstract getAllByOrgId(orgId: string): Promise<Array<OrganizationDomainResponse>>;
|
||||
abstract getByOrgIdAndOrgDomainId(
|
||||
orgId: string,
|
||||
orgDomainId: string,
|
||||
) => Promise<OrganizationDomainResponse>;
|
||||
post: (
|
||||
): Promise<OrganizationDomainResponse>;
|
||||
abstract post(
|
||||
orgId: string,
|
||||
orgDomain: OrganizationDomainRequest,
|
||||
) => Promise<OrganizationDomainResponse>;
|
||||
verify: (orgId: string, orgDomainId: string) => Promise<OrganizationDomainResponse>;
|
||||
delete: (orgId: string, orgDomainId: string) => Promise<any>;
|
||||
getClaimedOrgDomainByEmail: (email: string) => Promise<OrganizationDomainSsoDetailsResponse>;
|
||||
getVerifiedOrgDomainsByEmail: (
|
||||
): Promise<OrganizationDomainResponse>;
|
||||
abstract verify(orgId: string, orgDomainId: string): Promise<OrganizationDomainResponse>;
|
||||
abstract delete(orgId: string, orgDomainId: string): Promise<any>;
|
||||
abstract getClaimedOrgDomainByEmail(email: string): Promise<OrganizationDomainSsoDetailsResponse>;
|
||||
abstract getVerifiedOrgDomainsByEmail(
|
||||
email: string,
|
||||
) => Promise<ListResponse<VerifiedOrganizationDomainSsoDetailsResponse>>;
|
||||
): Promise<ListResponse<VerifiedOrganizationDomainSsoDetailsResponse>>;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { OrganizationDomainResponse } from "./responses/organization-domain.response";
|
||||
|
||||
export abstract class OrgDomainServiceAbstraction {
|
||||
orgDomains$: Observable<OrganizationDomainResponse[]>;
|
||||
abstract orgDomains$: Observable<OrganizationDomainResponse[]>;
|
||||
|
||||
get: (orgDomainId: string) => OrganizationDomainResponse;
|
||||
abstract get(orgDomainId: string): OrganizationDomainResponse;
|
||||
|
||||
copyDnsTxt: (dnsTxt: string) => void;
|
||||
abstract copyDnsTxt(dnsTxt: string): void;
|
||||
}
|
||||
|
||||
// Note: this separate class is designed to hold methods that are not
|
||||
// meant to be used in components (e.g., data write methods)
|
||||
export abstract class OrgDomainInternalServiceAbstraction extends OrgDomainServiceAbstraction {
|
||||
upsert: (orgDomains: OrganizationDomainResponse[]) => void;
|
||||
replace: (orgDomains: OrganizationDomainResponse[]) => void;
|
||||
clearCache: () => void;
|
||||
delete: (orgDomainIds: string[]) => void;
|
||||
abstract upsert(orgDomains: OrganizationDomainResponse[]): void;
|
||||
abstract replace(orgDomains: OrganizationDomainResponse[]): void;
|
||||
abstract clearCache(): void;
|
||||
abstract delete(orgDomainIds: string[]): void;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { OrganizationApiKeyRequest } from "../../../admin-console/models/request/organization-api-key.request";
|
||||
import { OrganizationSsoRequest } from "../../../auth/models/request/organization-sso.request";
|
||||
import { SecretVerificationRequest } from "../../../auth/models/request/secret-verification.request";
|
||||
@@ -34,60 +32,66 @@ import { OrganizationKeysResponse } from "../../models/response/organization-key
|
||||
import { OrganizationResponse } from "../../models/response/organization.response";
|
||||
import { ProfileOrganizationResponse } from "../../models/response/profile-organization.response";
|
||||
|
||||
export class OrganizationApiServiceAbstraction {
|
||||
get: (id: string) => Promise<OrganizationResponse>;
|
||||
getBilling: (id: string) => Promise<BillingResponse>;
|
||||
getBillingHistory: (id: string) => Promise<BillingHistoryResponse>;
|
||||
getSubscription: (id: string) => Promise<OrganizationSubscriptionResponse>;
|
||||
getLicense: (id: string, installationId: string) => Promise<unknown>;
|
||||
getAutoEnrollStatus: (identifier: string) => Promise<OrganizationAutoEnrollStatusResponse>;
|
||||
create: (request: OrganizationCreateRequest) => Promise<OrganizationResponse>;
|
||||
createWithoutPayment: (
|
||||
export abstract class OrganizationApiServiceAbstraction {
|
||||
abstract get(id: string): Promise<OrganizationResponse>;
|
||||
abstract getBilling(id: string): Promise<BillingResponse>;
|
||||
abstract getBillingHistory(id: string): Promise<BillingHistoryResponse>;
|
||||
abstract getSubscription(id: string): Promise<OrganizationSubscriptionResponse>;
|
||||
abstract getLicense(id: string, installationId: string): Promise<unknown>;
|
||||
abstract getAutoEnrollStatus(identifier: string): Promise<OrganizationAutoEnrollStatusResponse>;
|
||||
abstract create(request: OrganizationCreateRequest): Promise<OrganizationResponse>;
|
||||
abstract createWithoutPayment(
|
||||
request: OrganizationNoPaymentMethodCreateRequest,
|
||||
) => Promise<OrganizationResponse>;
|
||||
createLicense: (data: FormData) => Promise<OrganizationResponse>;
|
||||
save: (id: string, request: OrganizationUpdateRequest) => Promise<OrganizationResponse>;
|
||||
updatePayment: (id: string, request: PaymentRequest) => Promise<void>;
|
||||
upgrade: (id: string, request: OrganizationUpgradeRequest) => Promise<PaymentResponse>;
|
||||
updatePasswordManagerSeats: (
|
||||
): Promise<OrganizationResponse>;
|
||||
abstract createLicense(data: FormData): Promise<OrganizationResponse>;
|
||||
abstract save(id: string, request: OrganizationUpdateRequest): Promise<OrganizationResponse>;
|
||||
abstract updatePayment(id: string, request: PaymentRequest): Promise<void>;
|
||||
abstract upgrade(id: string, request: OrganizationUpgradeRequest): Promise<PaymentResponse>;
|
||||
abstract updatePasswordManagerSeats(
|
||||
id: string,
|
||||
request: OrganizationSubscriptionUpdateRequest,
|
||||
) => Promise<ProfileOrganizationResponse>;
|
||||
updateSecretsManagerSubscription: (
|
||||
): Promise<ProfileOrganizationResponse>;
|
||||
abstract updateSecretsManagerSubscription(
|
||||
id: string,
|
||||
request: OrganizationSmSubscriptionUpdateRequest,
|
||||
) => Promise<ProfileOrganizationResponse>;
|
||||
updateSeats: (id: string, request: SeatRequest) => Promise<PaymentResponse>;
|
||||
updateStorage: (id: string, request: StorageRequest) => Promise<PaymentResponse>;
|
||||
verifyBank: (id: string, request: VerifyBankRequest) => Promise<void>;
|
||||
reinstate: (id: string) => Promise<void>;
|
||||
leave: (id: string) => Promise<void>;
|
||||
delete: (id: string, request: SecretVerificationRequest) => Promise<void>;
|
||||
deleteUsingToken: (
|
||||
): Promise<ProfileOrganizationResponse>;
|
||||
abstract updateSeats(id: string, request: SeatRequest): Promise<PaymentResponse>;
|
||||
abstract updateStorage(id: string, request: StorageRequest): Promise<PaymentResponse>;
|
||||
abstract verifyBank(id: string, request: VerifyBankRequest): Promise<void>;
|
||||
abstract reinstate(id: string): Promise<void>;
|
||||
abstract leave(id: string): Promise<void>;
|
||||
abstract delete(id: string, request: SecretVerificationRequest): Promise<void>;
|
||||
abstract deleteUsingToken(
|
||||
organizationId: string,
|
||||
request: OrganizationVerifyDeleteRecoverRequest,
|
||||
) => Promise<any>;
|
||||
updateLicense: (id: string, data: FormData) => Promise<void>;
|
||||
importDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<void>;
|
||||
getOrCreateApiKey: (id: string, request: OrganizationApiKeyRequest) => Promise<ApiKeyResponse>;
|
||||
getApiKeyInformation: (
|
||||
): Promise<any>;
|
||||
abstract updateLicense(id: string, data: FormData): Promise<void>;
|
||||
abstract importDirectory(organizationId: string, request: ImportDirectoryRequest): Promise<void>;
|
||||
abstract getOrCreateApiKey(
|
||||
id: string,
|
||||
request: OrganizationApiKeyRequest,
|
||||
): Promise<ApiKeyResponse>;
|
||||
abstract getApiKeyInformation(
|
||||
id: string,
|
||||
organizationApiKeyType?: OrganizationApiKeyType,
|
||||
) => Promise<ListResponse<OrganizationApiKeyInformationResponse>>;
|
||||
rotateApiKey: (id: string, request: OrganizationApiKeyRequest) => Promise<ApiKeyResponse>;
|
||||
getTaxInfo: (id: string) => Promise<TaxInfoResponse>;
|
||||
updateTaxInfo: (id: string, request: ExpandedTaxInfoUpdateRequest) => Promise<void>;
|
||||
getKeys: (id: string) => Promise<OrganizationKeysResponse>;
|
||||
updateKeys: (id: string, request: OrganizationKeysRequest) => Promise<OrganizationKeysResponse>;
|
||||
getSso: (id: string) => Promise<OrganizationSsoResponse>;
|
||||
updateSso: (id: string, request: OrganizationSsoRequest) => Promise<OrganizationSsoResponse>;
|
||||
selfHostedSyncLicense: (id: string) => Promise<void>;
|
||||
subscribeToSecretsManager: (
|
||||
): Promise<ListResponse<OrganizationApiKeyInformationResponse>>;
|
||||
abstract rotateApiKey(id: string, request: OrganizationApiKeyRequest): Promise<ApiKeyResponse>;
|
||||
abstract getTaxInfo(id: string): Promise<TaxInfoResponse>;
|
||||
abstract updateTaxInfo(id: string, request: ExpandedTaxInfoUpdateRequest): Promise<void>;
|
||||
abstract getKeys(id: string): Promise<OrganizationKeysResponse>;
|
||||
abstract updateKeys(
|
||||
id: string,
|
||||
request: OrganizationKeysRequest,
|
||||
): Promise<OrganizationKeysResponse>;
|
||||
abstract getSso(id: string): Promise<OrganizationSsoResponse>;
|
||||
abstract updateSso(id: string, request: OrganizationSsoRequest): Promise<OrganizationSsoResponse>;
|
||||
abstract selfHostedSyncLicense(id: string): Promise<void>;
|
||||
abstract subscribeToSecretsManager(
|
||||
id: string,
|
||||
request: SecretsManagerSubscribeRequest,
|
||||
) => Promise<ProfileOrganizationResponse>;
|
||||
updateCollectionManagement: (
|
||||
): Promise<ProfileOrganizationResponse>;
|
||||
abstract updateCollectionManagement(
|
||||
id: string,
|
||||
request: OrganizationCollectionManagementUpdateRequest,
|
||||
) => Promise<OrganizationResponse>;
|
||||
): Promise<OrganizationResponse>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { map, Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "../../../types/guid";
|
||||
@@ -68,20 +66,20 @@ export abstract class OrganizationService {
|
||||
* Publishes state for all organizations under the specified user.
|
||||
* @returns An observable list of organizations
|
||||
*/
|
||||
organizations$: (userId: UserId) => Observable<Organization[]>;
|
||||
abstract organizations$(userId: UserId): Observable<Organization[]>;
|
||||
|
||||
// @todo Clean these up. Continuing to expand them is not recommended.
|
||||
// @see https://bitwarden.atlassian.net/browse/AC-2252
|
||||
memberOrganizations$: (userId: UserId) => Observable<Organization[]>;
|
||||
abstract memberOrganizations$(userId: UserId): Observable<Organization[]>;
|
||||
/**
|
||||
* Emits true if the user can create or manage a Free Bitwarden Families sponsorship.
|
||||
*/
|
||||
canManageSponsorships$: (userId: UserId) => Observable<boolean>;
|
||||
abstract canManageSponsorships$(userId: UserId): Observable<boolean>;
|
||||
/**
|
||||
* Emits true if any of the user's organizations have a Free Bitwarden Families sponsorship available.
|
||||
*/
|
||||
familySponsorshipAvailable$: (userId: UserId) => Observable<boolean>;
|
||||
hasOrganizations: (userId: UserId) => Observable<boolean>;
|
||||
abstract familySponsorshipAvailable$(userId: UserId): Observable<boolean>;
|
||||
abstract hasOrganizations(userId: UserId): Observable<boolean>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,7 +94,7 @@ export abstract class InternalOrganizationServiceAbstraction extends Organizatio
|
||||
* @param organization The organization state being saved.
|
||||
* @param userId The userId to replace state for.
|
||||
*/
|
||||
upsert: (OrganizationData: OrganizationData, userId: UserId) => Promise<void>;
|
||||
abstract upsert(OrganizationData: OrganizationData, userId: UserId): Promise<void>;
|
||||
|
||||
/**
|
||||
* Replaces state for the entire registered organization list for the specified user.
|
||||
@@ -107,5 +105,8 @@ export abstract class InternalOrganizationServiceAbstraction extends Organizatio
|
||||
* user.
|
||||
* @param userId The userId to replace state for.
|
||||
*/
|
||||
replace: (organizations: { [id: string]: OrganizationData }, userId: UserId) => Promise<void>;
|
||||
abstract replace(
|
||||
organizations: { [id: string]: OrganizationData },
|
||||
userId: UserId,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "../../types/guid";
|
||||
@@ -7,8 +5,8 @@ import { ProviderData } from "../models/data/provider.data";
|
||||
import { Provider } from "../models/domain/provider";
|
||||
|
||||
export abstract class ProviderService {
|
||||
get$: (id: string) => Observable<Provider>;
|
||||
get: (id: string) => Promise<Provider>;
|
||||
getAll: () => Promise<Provider[]>;
|
||||
save: (providers: { [id: string]: ProviderData }, userId?: UserId) => Promise<any>;
|
||||
abstract get$(id: string): Observable<Provider>;
|
||||
abstract get(id: string): Promise<Provider>;
|
||||
abstract getAll(): Promise<Provider[]>;
|
||||
abstract save(providers: { [id: string]: ProviderData }, userId?: UserId): Promise<any>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { AddableOrganizationResponse } from "@bitwarden/common/admin-console/models/response/addable-organization.response";
|
||||
|
||||
import { ProviderSetupRequest } from "../../models/request/provider/provider-setup.request";
|
||||
@@ -7,21 +5,23 @@ import { ProviderUpdateRequest } from "../../models/request/provider/provider-up
|
||||
import { ProviderVerifyRecoverDeleteRequest } from "../../models/request/provider/provider-verify-recover-delete.request";
|
||||
import { ProviderResponse } from "../../models/response/provider/provider.response";
|
||||
|
||||
export class ProviderApiServiceAbstraction {
|
||||
postProviderSetup: (id: string, request: ProviderSetupRequest) => Promise<ProviderResponse>;
|
||||
getProvider: (id: string) => Promise<ProviderResponse>;
|
||||
putProvider: (id: string, request: ProviderUpdateRequest) => Promise<ProviderResponse>;
|
||||
providerRecoverDeleteToken: (
|
||||
export abstract class ProviderApiServiceAbstraction {
|
||||
abstract postProviderSetup(id: string, request: ProviderSetupRequest): Promise<ProviderResponse>;
|
||||
abstract getProvider(id: string): Promise<ProviderResponse>;
|
||||
abstract putProvider(id: string, request: ProviderUpdateRequest): Promise<ProviderResponse>;
|
||||
abstract providerRecoverDeleteToken(
|
||||
organizationId: string,
|
||||
request: ProviderVerifyRecoverDeleteRequest,
|
||||
) => Promise<any>;
|
||||
deleteProvider: (id: string) => Promise<void>;
|
||||
getProviderAddableOrganizations: (providerId: string) => Promise<AddableOrganizationResponse[]>;
|
||||
addOrganizationToProvider: (
|
||||
): Promise<any>;
|
||||
abstract deleteProvider(id: string): Promise<void>;
|
||||
abstract getProviderAddableOrganizations(
|
||||
providerId: string,
|
||||
): Promise<AddableOrganizationResponse[]>;
|
||||
abstract addOrganizationToProvider(
|
||||
providerId: string,
|
||||
request: {
|
||||
key: string;
|
||||
organizationId: string;
|
||||
},
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "../../types/guid";
|
||||
@@ -35,20 +33,20 @@ export function accountInfoEqual(a: AccountInfo, b: AccountInfo) {
|
||||
}
|
||||
|
||||
export abstract class AccountService {
|
||||
accounts$: Observable<Record<UserId, AccountInfo>>;
|
||||
abstract accounts$: Observable<Record<UserId, AccountInfo>>;
|
||||
|
||||
activeAccount$: Observable<Account | null>;
|
||||
abstract activeAccount$: Observable<Account | null>;
|
||||
|
||||
/**
|
||||
* Observable of the last activity time for each account.
|
||||
*/
|
||||
accountActivity$: Observable<Record<UserId, Date>>;
|
||||
abstract accountActivity$: Observable<Record<UserId, Date>>;
|
||||
/** Observable of the new device login verification property for the account. */
|
||||
accountVerifyNewDeviceLogin$: Observable<boolean>;
|
||||
abstract accountVerifyNewDeviceLogin$: Observable<boolean>;
|
||||
/** Account list in order of descending recency */
|
||||
sortedUserIds$: Observable<UserId[]>;
|
||||
abstract sortedUserIds$: Observable<UserId[]>;
|
||||
/** Next account that is not the current active account */
|
||||
nextUpAccount$: Observable<Account>;
|
||||
abstract nextUpAccount$: Observable<Account>;
|
||||
/**
|
||||
* Updates the `accounts$` observable with the new account data.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
export abstract class AnonymousHubService {
|
||||
createHubConnection: (token: string) => Promise<void>;
|
||||
stopHubConnection: () => Promise<void>;
|
||||
abstract createHubConnection(token: string): Promise<void>;
|
||||
abstract stopHubConnection(): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "../../types/guid";
|
||||
@@ -9,7 +7,7 @@ export abstract class AvatarService {
|
||||
* An observable monitoring the active user's avatar color.
|
||||
* The observable updates when the avatar color changes.
|
||||
*/
|
||||
avatarColor$: Observable<string | null>;
|
||||
abstract avatarColor$: Observable<string | null>;
|
||||
/**
|
||||
* Sets the avatar color of the active user
|
||||
*
|
||||
|
||||
@@ -1,47 +1,45 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { ListResponse } from "../../models/response/list.response";
|
||||
import { DeviceResponse } from "../abstractions/devices/responses/device.response";
|
||||
import { UpdateDevicesTrustRequest } from "../models/request/update-devices-trust.request";
|
||||
import { ProtectedDeviceResponse } from "../models/response/protected-device.response";
|
||||
|
||||
export abstract class DevicesApiServiceAbstraction {
|
||||
getKnownDevice: (email: string, deviceIdentifier: string) => Promise<boolean>;
|
||||
abstract getKnownDevice(email: string, deviceIdentifier: string): Promise<boolean>;
|
||||
|
||||
getDeviceByIdentifier: (deviceIdentifier: string) => Promise<DeviceResponse>;
|
||||
abstract getDeviceByIdentifier(deviceIdentifier: string): Promise<DeviceResponse>;
|
||||
|
||||
getDevices: () => Promise<ListResponse<DeviceResponse>>;
|
||||
abstract getDevices(): Promise<ListResponse<DeviceResponse>>;
|
||||
|
||||
updateTrustedDeviceKeys: (
|
||||
abstract updateTrustedDeviceKeys(
|
||||
deviceIdentifier: string,
|
||||
devicePublicKeyEncryptedUserKey: string,
|
||||
userKeyEncryptedDevicePublicKey: string,
|
||||
deviceKeyEncryptedDevicePrivateKey: string,
|
||||
) => Promise<DeviceResponse>;
|
||||
): Promise<DeviceResponse>;
|
||||
|
||||
updateTrust: (
|
||||
abstract updateTrust(
|
||||
updateDevicesTrustRequestModel: UpdateDevicesTrustRequest,
|
||||
deviceIdentifier: string,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
getDeviceKeys: (deviceIdentifier: string) => Promise<ProtectedDeviceResponse>;
|
||||
abstract getDeviceKeys(deviceIdentifier: string): Promise<ProtectedDeviceResponse>;
|
||||
|
||||
/**
|
||||
* Notifies the server that the device has a device key, but didn't receive any associated decryption keys.
|
||||
* Note: For debugging purposes only.
|
||||
* @param deviceIdentifier - current device identifier
|
||||
*/
|
||||
postDeviceTrustLoss: (deviceIdentifier: string) => Promise<void>;
|
||||
abstract postDeviceTrustLoss(deviceIdentifier: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Deactivates a device
|
||||
* @param deviceId - The device ID
|
||||
*/
|
||||
deactivateDevice: (deviceId: string) => Promise<void>;
|
||||
abstract deactivateDevice(deviceId: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes trust from a list of devices
|
||||
* @param deviceIds - The device IDs to be untrusted
|
||||
*/
|
||||
untrustDevices: (deviceIds: string[]) => Promise<void>;
|
||||
abstract untrustDevices(deviceIds: string[]): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { VaultTimeout, VaultTimeoutAction } from "../../key-management/vault-timeout";
|
||||
@@ -27,20 +25,20 @@ export abstract class TokenService {
|
||||
*
|
||||
* @returns A promise that resolves with the SetTokensResult containing the tokens that were set.
|
||||
*/
|
||||
setTokens: (
|
||||
abstract setTokens(
|
||||
accessToken: string,
|
||||
vaultTimeoutAction: VaultTimeoutAction,
|
||||
vaultTimeout: VaultTimeout,
|
||||
refreshToken?: string,
|
||||
clientIdClientSecret?: [string, string],
|
||||
) => Promise<SetTokensResult>;
|
||||
): Promise<SetTokensResult>;
|
||||
|
||||
/**
|
||||
* Clears the access token, refresh token, API Key Client ID, and API Key Client Secret out of memory, disk, and secure storage if supported.
|
||||
* @param userId The optional user id to clear the tokens for; if not provided, the active user id is used.
|
||||
* @returns A promise that resolves when the tokens have been cleared.
|
||||
*/
|
||||
clearTokens: (userId?: UserId) => Promise<void>;
|
||||
abstract clearTokens(userId?: UserId): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sets the access token in memory or disk based on the given vaultTimeoutAction and vaultTimeout
|
||||
@@ -51,11 +49,11 @@ export abstract class TokenService {
|
||||
* @param vaultTimeout The timeout for the vault.
|
||||
* @returns A promise that resolves with the access token that has been set.
|
||||
*/
|
||||
setAccessToken: (
|
||||
abstract setAccessToken(
|
||||
accessToken: string,
|
||||
vaultTimeoutAction: VaultTimeoutAction,
|
||||
vaultTimeout: VaultTimeout,
|
||||
) => Promise<string>;
|
||||
): Promise<string>;
|
||||
|
||||
// TODO: revisit having this public clear method approach once the state service is fully deprecated.
|
||||
/**
|
||||
@@ -67,21 +65,21 @@ export abstract class TokenService {
|
||||
* pass in the vaultTimeoutAction and vaultTimeout.
|
||||
* This avoids a circular dependency between the StateService, TokenService, and VaultTimeoutSettingsService.
|
||||
*/
|
||||
clearAccessToken: (userId?: UserId) => Promise<void>;
|
||||
abstract clearAccessToken(userId?: UserId): Promise<void>;
|
||||
|
||||
/**
|
||||
* Gets the access token
|
||||
* @param userId - The optional user id to get the access token for; if not provided, the active user is used.
|
||||
* @returns A promise that resolves with the access token or null.
|
||||
*/
|
||||
getAccessToken: (userId?: UserId) => Promise<string | null>;
|
||||
abstract getAccessToken(userId?: UserId): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* Gets the refresh token.
|
||||
* @param userId - The optional user id to get the refresh token for; if not provided, the active user is used.
|
||||
* @returns A promise that resolves with the refresh token or null.
|
||||
*/
|
||||
getRefreshToken: (userId?: UserId) => Promise<string | null>;
|
||||
abstract getRefreshToken(userId?: UserId): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* Sets the API Key Client ID for the active user id in memory or disk based on the given vaultTimeoutAction and vaultTimeout.
|
||||
@@ -90,18 +88,18 @@ export abstract class TokenService {
|
||||
* @param vaultTimeout The timeout for the vault.
|
||||
* @returns A promise that resolves with the API Key Client ID that has been set.
|
||||
*/
|
||||
setClientId: (
|
||||
abstract setClientId(
|
||||
clientId: string,
|
||||
vaultTimeoutAction: VaultTimeoutAction,
|
||||
vaultTimeout: VaultTimeout,
|
||||
userId?: UserId,
|
||||
) => Promise<string>;
|
||||
): Promise<string>;
|
||||
|
||||
/**
|
||||
* Gets the API Key Client ID for the active user.
|
||||
* @returns A promise that resolves with the API Key Client ID or undefined
|
||||
*/
|
||||
getClientId: (userId?: UserId) => Promise<string | undefined>;
|
||||
abstract getClientId(userId?: UserId): Promise<string | undefined>;
|
||||
|
||||
/**
|
||||
* Sets the API Key Client Secret for the active user id in memory or disk based on the given vaultTimeoutAction and vaultTimeout.
|
||||
@@ -110,18 +108,18 @@ export abstract class TokenService {
|
||||
* @param vaultTimeout The timeout for the vault.
|
||||
* @returns A promise that resolves with the client secret that has been set.
|
||||
*/
|
||||
setClientSecret: (
|
||||
abstract setClientSecret(
|
||||
clientSecret: string,
|
||||
vaultTimeoutAction: VaultTimeoutAction,
|
||||
vaultTimeout: VaultTimeout,
|
||||
userId?: UserId,
|
||||
) => Promise<string>;
|
||||
): Promise<string>;
|
||||
|
||||
/**
|
||||
* Gets the API Key Client Secret for the active user.
|
||||
* @returns A promise that resolves with the API Key Client Secret or undefined
|
||||
*/
|
||||
getClientSecret: (userId?: UserId) => Promise<string | undefined>;
|
||||
abstract getClientSecret(userId?: UserId): Promise<string | undefined>;
|
||||
|
||||
/**
|
||||
* Sets the two factor token for the given email in global state.
|
||||
@@ -131,21 +129,21 @@ export abstract class TokenService {
|
||||
* @param twoFactorToken The two factor token to set.
|
||||
* @returns A promise that resolves when the two factor token has been set.
|
||||
*/
|
||||
setTwoFactorToken: (email: string, twoFactorToken: string) => Promise<void>;
|
||||
abstract setTwoFactorToken(email: string, twoFactorToken: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Gets the two factor token for the given email.
|
||||
* @param email The email to get the two factor token for.
|
||||
* @returns A promise that resolves with the two factor token for the given email or null if it isn't found.
|
||||
*/
|
||||
getTwoFactorToken: (email: string) => Promise<string | null>;
|
||||
abstract getTwoFactorToken(email: string): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* Clears the two factor token for the given email out of global state.
|
||||
* @param email The email to clear the two factor token for.
|
||||
* @returns A promise that resolves when the two factor token has been cleared.
|
||||
*/
|
||||
clearTwoFactorToken: (email: string) => Promise<void>;
|
||||
abstract clearTwoFactorToken(email: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Decodes the access token.
|
||||
@@ -153,13 +151,13 @@ export abstract class TokenService {
|
||||
* If null, the currently active user's token is used.
|
||||
* @returns A promise that resolves with the decoded access token.
|
||||
*/
|
||||
decodeAccessToken: (tokenOrUserId?: string | UserId) => Promise<DecodedAccessToken>;
|
||||
abstract decodeAccessToken(tokenOrUserId?: string | UserId): Promise<DecodedAccessToken>;
|
||||
|
||||
/**
|
||||
* Gets the expiration date for the access token. Returns if token can't be decoded or has no expiration
|
||||
* @returns A promise that resolves with the expiration date for the access token.
|
||||
*/
|
||||
getTokenExpirationDate: () => Promise<Date | null>;
|
||||
abstract getTokenExpirationDate(): Promise<Date | null>;
|
||||
|
||||
/**
|
||||
* Calculates the adjusted time in seconds until the access token expires, considering an optional offset.
|
||||
@@ -170,58 +168,58 @@ export abstract class TokenService {
|
||||
* based on the actual expiration.
|
||||
* @returns {Promise<number>} Promise resolving to the adjusted seconds remaining.
|
||||
*/
|
||||
tokenSecondsRemaining: (offsetSeconds?: number) => Promise<number>;
|
||||
abstract tokenSecondsRemaining(offsetSeconds?: number): Promise<number>;
|
||||
|
||||
/**
|
||||
* Checks if the access token needs to be refreshed.
|
||||
* @param {number} [minutes=5] - Optional number of minutes before the access token expires to consider refreshing it.
|
||||
* @returns A promise that resolves with a boolean indicating if the access token needs to be refreshed.
|
||||
*/
|
||||
tokenNeedsRefresh: (minutes?: number) => Promise<boolean>;
|
||||
abstract tokenNeedsRefresh(minutes?: number): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Gets the user id for the active user from the access token.
|
||||
* @returns A promise that resolves with the user id for the active user.
|
||||
* @deprecated Use AccountService.activeAccount$ instead.
|
||||
*/
|
||||
getUserId: () => Promise<UserId>;
|
||||
abstract getUserId(): Promise<UserId>;
|
||||
|
||||
/**
|
||||
* Gets the email for the active user from the access token.
|
||||
* @returns A promise that resolves with the email for the active user.
|
||||
* @deprecated Use AccountService.activeAccount$ instead.
|
||||
*/
|
||||
getEmail: () => Promise<string>;
|
||||
abstract getEmail(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Gets the email verified status for the active user from the access token.
|
||||
* @returns A promise that resolves with the email verified status for the active user.
|
||||
*/
|
||||
getEmailVerified: () => Promise<boolean>;
|
||||
abstract getEmailVerified(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Gets the name for the active user from the access token.
|
||||
* @returns A promise that resolves with the name for the active user.
|
||||
* @deprecated Use AccountService.activeAccount$ instead.
|
||||
*/
|
||||
getName: () => Promise<string>;
|
||||
abstract getName(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Gets the issuer for the active user from the access token.
|
||||
* @returns A promise that resolves with the issuer for the active user.
|
||||
*/
|
||||
getIssuer: () => Promise<string>;
|
||||
abstract getIssuer(): Promise<string>;
|
||||
|
||||
/**
|
||||
* Gets whether or not the user authenticated via an external mechanism.
|
||||
* @param userId The optional user id to check for external authN status; if not provided, the active user is used.
|
||||
* @returns A promise that resolves with a boolean representing the user's external authN status.
|
||||
*/
|
||||
getIsExternal: (userId: UserId) => Promise<boolean>;
|
||||
abstract getIsExternal(userId: UserId): Promise<boolean>;
|
||||
|
||||
/** Gets the active or passed in user's security stamp */
|
||||
getSecurityStamp: (userId?: UserId) => Promise<string | null>;
|
||||
abstract getSecurityStamp(userId?: UserId): Promise<string | null>;
|
||||
|
||||
/** Sets the security stamp for the active or passed in user */
|
||||
setSecurityStamp: (securityStamp: string, userId?: UserId) => Promise<void>;
|
||||
abstract setSecurityStamp(securityStamp: string, userId?: UserId): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { SecretVerificationRequest } from "../../models/request/secret-verification.request";
|
||||
import { VerifyOTPRequest } from "../../models/request/verify-otp.request";
|
||||
import { MasterPasswordPolicyResponse } from "../../models/response/master-password-policy.response";
|
||||
|
||||
export abstract class UserVerificationApiServiceAbstraction {
|
||||
postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise<void>;
|
||||
postAccountRequestOTP: () => Promise<void>;
|
||||
postAccountVerifyPassword: (
|
||||
abstract postAccountVerifyOTP(request: VerifyOTPRequest): Promise<void>;
|
||||
abstract postAccountRequestOTP(): Promise<void>;
|
||||
abstract postAccountVerifyPassword(
|
||||
request: SecretVerificationRequest,
|
||||
) => Promise<MasterPasswordPolicyResponse>;
|
||||
): Promise<MasterPasswordPolicyResponse>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { SecretVerificationRequest } from "../../models/request/secret-verification.request";
|
||||
import { UserVerificationOptions } from "../../types/user-verification-options";
|
||||
@@ -16,9 +14,9 @@ export abstract class UserVerificationService {
|
||||
* @param verificationType Type of verification to restrict the options to
|
||||
* @returns Available verification options for the user
|
||||
*/
|
||||
getAvailableVerificationOptions: (
|
||||
abstract getAvailableVerificationOptions(
|
||||
verificationType: keyof UserVerificationOptions,
|
||||
) => Promise<UserVerificationOptions>;
|
||||
): Promise<UserVerificationOptions>;
|
||||
/**
|
||||
* Create a new request model to be used for server-side verification
|
||||
* @param verification User-supplied verification data (Master Password or OTP)
|
||||
@@ -26,11 +24,11 @@ export abstract class UserVerificationService {
|
||||
* @param alreadyHashed Whether the master password is already hashed
|
||||
* @throws Error if the verification data is invalid
|
||||
*/
|
||||
buildRequest: <T extends SecretVerificationRequest>(
|
||||
abstract buildRequest<T extends SecretVerificationRequest>(
|
||||
verification: Verification,
|
||||
requestClass?: new () => T,
|
||||
alreadyHashed?: boolean,
|
||||
) => Promise<T>;
|
||||
): Promise<T>;
|
||||
/**
|
||||
* Verifies the user using the provided verification data.
|
||||
* PIN or biometrics are verified client-side.
|
||||
@@ -39,11 +37,11 @@ export abstract class UserVerificationService {
|
||||
* @param verification User-supplied verification data (OTP, MP, PIN, or biometrics)
|
||||
* @throws Error if the verification data is invalid or the verification fails
|
||||
*/
|
||||
verifyUser: (verification: Verification) => Promise<boolean>;
|
||||
abstract verifyUser(verification: Verification): Promise<boolean>;
|
||||
/**
|
||||
* Request a one-time password (OTP) to be sent to the user's email
|
||||
*/
|
||||
requestOTP: () => Promise<void>;
|
||||
abstract requestOTP(): Promise<void>;
|
||||
/**
|
||||
* Check if user has master password or can only use passwordless technologies to log in
|
||||
* Note: This only checks the server, not the local state
|
||||
@@ -51,13 +49,13 @@ export abstract class UserVerificationService {
|
||||
* @returns True if the user has a master password
|
||||
* @deprecated Use UserDecryptionOptionsService.hasMasterPassword$ instead
|
||||
*/
|
||||
hasMasterPassword: (userId?: string) => Promise<boolean>;
|
||||
abstract hasMasterPassword(userId?: string): Promise<boolean>;
|
||||
/**
|
||||
* Check if the user has a master password and has used it during their current session
|
||||
* @param userId The user id to check. If not provided, the current user id used
|
||||
* @returns True if the user has a master password and has used it in the current session
|
||||
*/
|
||||
hasMasterPasswordAndMasterKeyHash: (userId?: string) => Promise<boolean>;
|
||||
abstract hasMasterPasswordAndMasterKeyHash(userId?: string): Promise<boolean>;
|
||||
/**
|
||||
* Verifies the user using the provided master password.
|
||||
* Attempts to verify client-side first, then server-side if necessary.
|
||||
@@ -68,9 +66,9 @@ export abstract class UserVerificationService {
|
||||
* @throws Error if the master password is invalid
|
||||
* @returns An object containing the master key, and master password policy options if verified on server.
|
||||
*/
|
||||
verifyUserByMasterPassword: (
|
||||
abstract verifyUserByMasterPassword(
|
||||
verification: MasterPasswordVerification,
|
||||
userId: UserId,
|
||||
email: string,
|
||||
) => Promise<MasterPasswordVerificationResponse>;
|
||||
): Promise<MasterPasswordVerificationResponse>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { CredentialAssertionOptionsResponse } from "../../services/webauthn-login/response/credential-assertion-options.response";
|
||||
|
||||
export class WebAuthnLoginApiServiceAbstraction {
|
||||
getCredentialAssertionOptions: () => Promise<CredentialAssertionOptionsResponse>;
|
||||
export abstract class WebAuthnLoginApiServiceAbstraction {
|
||||
abstract getCredentialAssertionOptions(): Promise<CredentialAssertionOptionsResponse>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { PrfKey } from "../../../types/key";
|
||||
|
||||
/**
|
||||
@@ -9,11 +7,11 @@ export abstract class WebAuthnLoginPrfKeyServiceAbstraction {
|
||||
/**
|
||||
* Get the salt used to generate the PRF-output used when logging in with WebAuthn.
|
||||
*/
|
||||
getLoginWithPrfSalt: () => Promise<ArrayBuffer>;
|
||||
abstract getLoginWithPrfSalt(): Promise<ArrayBuffer>;
|
||||
|
||||
/**
|
||||
* Create a symmetric key from the PRF-output by stretching it.
|
||||
* This should be used as `ExternalKey` with `RotateableKeySet`.
|
||||
*/
|
||||
createSymmetricKeyFromPrf: (prf: ArrayBuffer) => Promise<PrfKey>;
|
||||
abstract createSymmetricKeyFromPrf(prf: ArrayBuffer): Promise<PrfKey>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { AuthResult } from "../../models/domain/auth-result";
|
||||
import { WebAuthnLoginCredentialAssertionOptionsView } from "../../models/view/webauthn-login/webauthn-login-credential-assertion-options.view";
|
||||
import { WebAuthnLoginCredentialAssertionView } from "../../models/view/webauthn-login/webauthn-login-credential-assertion.view";
|
||||
@@ -14,7 +12,7 @@ export abstract class WebAuthnLoginServiceAbstraction {
|
||||
* (whether FIDO2 user verification is required, the relying party id, timeout duration for the process to complete, etc.)
|
||||
* for the authenticator.
|
||||
*/
|
||||
getCredentialAssertionOptions: () => Promise<WebAuthnLoginCredentialAssertionOptionsView>;
|
||||
abstract getCredentialAssertionOptions(): Promise<WebAuthnLoginCredentialAssertionOptionsView>;
|
||||
|
||||
/**
|
||||
* Asserts the credential. This involves user interaction with the authenticator
|
||||
@@ -27,9 +25,9 @@ export abstract class WebAuthnLoginServiceAbstraction {
|
||||
* @returns {WebAuthnLoginCredentialAssertionView} The assertion obtained from the authenticator.
|
||||
* If the assertion is not successfully obtained, it returns undefined.
|
||||
*/
|
||||
assertCredential: (
|
||||
abstract assertCredential(
|
||||
credentialAssertionOptions: WebAuthnLoginCredentialAssertionOptionsView,
|
||||
) => Promise<WebAuthnLoginCredentialAssertionView | undefined>;
|
||||
): Promise<WebAuthnLoginCredentialAssertionView | undefined>;
|
||||
|
||||
/**
|
||||
* Logs the user in using the assertion obtained from the authenticator.
|
||||
@@ -39,5 +37,5 @@ export abstract class WebAuthnLoginServiceAbstraction {
|
||||
* @param {WebAuthnLoginCredentialAssertionView} assertion - The assertion obtained from the authenticator
|
||||
* that needs to be validated for login.
|
||||
*/
|
||||
logIn: (assertion: WebAuthnLoginCredentialAssertionView) => Promise<AuthResult>;
|
||||
abstract logIn(assertion: WebAuthnLoginCredentialAssertionView): Promise<AuthResult>;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import {
|
||||
BillingInvoiceResponse,
|
||||
BillingTransactionResponse,
|
||||
} from "../../models/response/billing.response";
|
||||
|
||||
export class AccountBillingApiServiceAbstraction {
|
||||
getBillingInvoices: (status?: string, startAfter?: string) => Promise<BillingInvoiceResponse[]>;
|
||||
getBillingTransactions: (startAfter?: string) => Promise<BillingTransactionResponse[]>;
|
||||
export abstract class AccountBillingApiServiceAbstraction {
|
||||
abstract getBillingInvoices(
|
||||
status?: string,
|
||||
startAfter?: string,
|
||||
): Promise<BillingInvoiceResponse[]>;
|
||||
abstract getBillingTransactions(startAfter?: string): Promise<BillingTransactionResponse[]>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "../../../types/guid";
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
||||
|
||||
import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request";
|
||||
@@ -20,78 +17,78 @@ import { PaymentMethodResponse } from "../models/response/payment-method.respons
|
||||
import { ProviderSubscriptionResponse } from "../models/response/provider-subscription-response";
|
||||
|
||||
export abstract class BillingApiServiceAbstraction {
|
||||
cancelOrganizationSubscription: (
|
||||
abstract cancelOrganizationSubscription(
|
||||
organizationId: string,
|
||||
request: SubscriptionCancellationRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
cancelPremiumUserSubscription: (request: SubscriptionCancellationRequest) => Promise<void>;
|
||||
abstract cancelPremiumUserSubscription(request: SubscriptionCancellationRequest): Promise<void>;
|
||||
|
||||
createProviderClientOrganization: (
|
||||
abstract createProviderClientOrganization(
|
||||
providerId: string,
|
||||
request: CreateClientOrganizationRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
createSetupIntent: (paymentMethodType: PaymentMethodType) => Promise<string>;
|
||||
abstract createSetupIntent(paymentMethodType: PaymentMethodType): Promise<string>;
|
||||
|
||||
getOrganizationBillingMetadata: (
|
||||
abstract getOrganizationBillingMetadata(
|
||||
organizationId: string,
|
||||
) => Promise<OrganizationBillingMetadataResponse>;
|
||||
): Promise<OrganizationBillingMetadataResponse>;
|
||||
|
||||
getOrganizationPaymentMethod: (organizationId: string) => Promise<PaymentMethodResponse>;
|
||||
abstract getOrganizationPaymentMethod(organizationId: string): Promise<PaymentMethodResponse>;
|
||||
|
||||
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
||||
abstract getPlans(): Promise<ListResponse<PlanResponse>>;
|
||||
|
||||
getProviderClientInvoiceReport: (providerId: string, invoiceId: string) => Promise<string>;
|
||||
abstract getProviderClientInvoiceReport(providerId: string, invoiceId: string): Promise<string>;
|
||||
|
||||
getProviderClientOrganizations: (
|
||||
abstract getProviderClientOrganizations(
|
||||
providerId: string,
|
||||
) => Promise<ListResponse<ProviderOrganizationOrganizationDetailsResponse>>;
|
||||
): Promise<ListResponse<ProviderOrganizationOrganizationDetailsResponse>>;
|
||||
|
||||
getProviderInvoices: (providerId: string) => Promise<InvoicesResponse>;
|
||||
abstract getProviderInvoices(providerId: string): Promise<InvoicesResponse>;
|
||||
|
||||
getProviderSubscription: (providerId: string) => Promise<ProviderSubscriptionResponse>;
|
||||
abstract getProviderSubscription(providerId: string): Promise<ProviderSubscriptionResponse>;
|
||||
|
||||
getProviderTaxInformation: (providerId: string) => Promise<TaxInfoResponse>;
|
||||
abstract getProviderTaxInformation(providerId: string): Promise<TaxInfoResponse>;
|
||||
|
||||
updateOrganizationPaymentMethod: (
|
||||
abstract updateOrganizationPaymentMethod(
|
||||
organizationId: string,
|
||||
request: UpdatePaymentMethodRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
updateOrganizationTaxInformation: (
|
||||
abstract updateOrganizationTaxInformation(
|
||||
organizationId: string,
|
||||
request: ExpandedTaxInfoUpdateRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
updateProviderClientOrganization: (
|
||||
abstract updateProviderClientOrganization(
|
||||
providerId: string,
|
||||
organizationId: string,
|
||||
request: UpdateClientOrganizationRequest,
|
||||
) => Promise<any>;
|
||||
): Promise<any>;
|
||||
|
||||
updateProviderPaymentMethod: (
|
||||
abstract updateProviderPaymentMethod(
|
||||
providerId: string,
|
||||
request: UpdatePaymentMethodRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
updateProviderTaxInformation: (
|
||||
abstract updateProviderTaxInformation(
|
||||
providerId: string,
|
||||
request: ExpandedTaxInfoUpdateRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
verifyOrganizationBankAccount: (
|
||||
abstract verifyOrganizationBankAccount(
|
||||
organizationId: string,
|
||||
request: VerifyBankAccountRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
verifyProviderBankAccount: (
|
||||
abstract verifyProviderBankAccount(
|
||||
providerId: string,
|
||||
request: VerifyBankAccountRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
restartSubscription: (
|
||||
abstract restartSubscription(
|
||||
organizationId: string,
|
||||
request: OrganizationCreateRequest,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
@@ -49,20 +47,22 @@ export type SubscriptionInformation = {
|
||||
};
|
||||
|
||||
export abstract class OrganizationBillingServiceAbstraction {
|
||||
getPaymentSource: (organizationId: string) => Promise<PaymentSourceResponse>;
|
||||
abstract getPaymentSource(organizationId: string): Promise<PaymentSourceResponse>;
|
||||
|
||||
purchaseSubscription: (subscription: SubscriptionInformation) => Promise<OrganizationResponse>;
|
||||
|
||||
purchaseSubscriptionNoPaymentMethod: (
|
||||
abstract purchaseSubscription(
|
||||
subscription: SubscriptionInformation,
|
||||
) => Promise<OrganizationResponse>;
|
||||
): Promise<OrganizationResponse>;
|
||||
|
||||
startFree: (subscription: SubscriptionInformation) => Promise<OrganizationResponse>;
|
||||
abstract purchaseSubscriptionNoPaymentMethod(
|
||||
subscription: SubscriptionInformation,
|
||||
): Promise<OrganizationResponse>;
|
||||
|
||||
restartSubscription: (
|
||||
abstract startFree(subscription: SubscriptionInformation): Promise<OrganizationResponse>;
|
||||
|
||||
abstract restartSubscription(
|
||||
organizationId: string,
|
||||
subscription: SubscriptionInformation,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Determines if breadcrumbing policies is enabled for the organizations meeting certain criteria.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ChangePlanFrequencyRequest } from "@bitwarden/common/billing/models/request/change-plan-frequency.request";
|
||||
import { OrganizationWarningsResponse } from "@bitwarden/common/billing/models/response/organization-warnings.response";
|
||||
|
||||
import {
|
||||
@@ -28,4 +29,9 @@ export abstract class OrganizationBillingApiServiceAbstraction {
|
||||
organizationKey: string;
|
||||
},
|
||||
) => Promise<string>;
|
||||
|
||||
abstract changeSubscriptionFrequency: (
|
||||
organizationId: string,
|
||||
request: ChangePlanFrequencyRequest,
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { PlanType } from "../../enums";
|
||||
|
||||
export class ChangePlanFrequencyRequest {
|
||||
newPlanType: PlanType;
|
||||
|
||||
constructor(newPlanType?: PlanType) {
|
||||
this.newPlanType = newPlanType!;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ChangePlanFrequencyRequest } from "@bitwarden/common/billing/models/request/change-plan-frequency.request";
|
||||
import { OrganizationWarningsResponse } from "@bitwarden/common/billing/models/response/organization-warnings.response";
|
||||
|
||||
import { ApiService } from "../../../abstractions/api.service";
|
||||
@@ -83,4 +84,17 @@ export class OrganizationBillingApiService implements OrganizationBillingApiServ
|
||||
|
||||
return response as string;
|
||||
}
|
||||
|
||||
async changeSubscriptionFrequency(
|
||||
organizationId: string,
|
||||
request: ChangePlanFrequencyRequest,
|
||||
): Promise<void> {
|
||||
return await this.apiService.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/billing/change-frequency",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { OtherDeviceKeysUpdateRequest } from "@bitwarden/common/auth/models/request/update-devices-trust.request";
|
||||
@@ -15,51 +13,51 @@ export abstract class DeviceTrustServiceAbstraction {
|
||||
* by Platform
|
||||
* @description Checks if the device trust feature is supported for the active user.
|
||||
*/
|
||||
supportsDeviceTrust$: Observable<boolean>;
|
||||
abstract supportsDeviceTrust$: Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Emits when a device has been trusted. This emission is specifically for the purpose of notifying
|
||||
* the consuming component to display a toast informing the user the device has been trusted.
|
||||
*/
|
||||
deviceTrusted$: Observable<void>;
|
||||
abstract deviceTrusted$: Observable<void>;
|
||||
|
||||
/**
|
||||
* @description Checks if the device trust feature is supported for the given user.
|
||||
*/
|
||||
supportsDeviceTrustByUserId$: (userId: UserId) => Observable<boolean>;
|
||||
abstract supportsDeviceTrustByUserId$(userId: UserId): Observable<boolean>;
|
||||
|
||||
/**
|
||||
* @description Retrieves the users choice to trust the device which can only happen after decryption
|
||||
* Note: this value should only be used once and then reset
|
||||
*/
|
||||
getShouldTrustDevice: (userId: UserId) => Promise<boolean | null>;
|
||||
setShouldTrustDevice: (userId: UserId, value: boolean) => Promise<void>;
|
||||
abstract getShouldTrustDevice(userId: UserId): Promise<boolean | null>;
|
||||
abstract setShouldTrustDevice(userId: UserId, value: boolean): Promise<void>;
|
||||
|
||||
trustDeviceIfRequired: (userId: UserId) => Promise<void>;
|
||||
abstract trustDeviceIfRequired(userId: UserId): Promise<void>;
|
||||
|
||||
trustDevice: (userId: UserId) => Promise<DeviceResponse>;
|
||||
abstract trustDevice(userId: UserId): Promise<DeviceResponse>;
|
||||
|
||||
/** Retrieves the device key if it exists from state or secure storage if supported for the active user. */
|
||||
getDeviceKey: (userId: UserId) => Promise<DeviceKey | null>;
|
||||
decryptUserKeyWithDeviceKey: (
|
||||
abstract getDeviceKey(userId: UserId): Promise<DeviceKey | null>;
|
||||
abstract decryptUserKeyWithDeviceKey(
|
||||
userId: UserId,
|
||||
encryptedDevicePrivateKey: EncString,
|
||||
encryptedUserKey: EncString,
|
||||
deviceKey: DeviceKey,
|
||||
) => Promise<UserKey | null>;
|
||||
rotateDevicesTrust: (
|
||||
): Promise<UserKey | null>;
|
||||
abstract rotateDevicesTrust(
|
||||
userId: UserId,
|
||||
newUserKey: UserKey,
|
||||
masterPasswordHash: string,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
/**
|
||||
* Notifies the server that the device has a device key, but didn't receive any associated decryption keys.
|
||||
* Note: For debugging purposes only.
|
||||
*/
|
||||
recordDeviceTrustLoss: () => Promise<void>;
|
||||
getRotatedData: (
|
||||
abstract recordDeviceTrustLoss(): Promise<void>;
|
||||
abstract getRotatedData(
|
||||
oldUserKey: UserKey,
|
||||
newUserKey: UserKey,
|
||||
userId: UserId,
|
||||
) => Promise<OtherDeviceKeysUpdateRequest[]>;
|
||||
): Promise<OtherDeviceKeysUpdateRequest[]>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "../../../types/guid";
|
||||
@@ -13,11 +11,11 @@ export abstract class VaultTimeoutSettingsService {
|
||||
* @param vaultTimeoutAction The vault timeout action
|
||||
* @param userId The user id to set the data for.
|
||||
*/
|
||||
setVaultTimeoutOptions: (
|
||||
abstract setVaultTimeoutOptions(
|
||||
userId: UserId,
|
||||
vaultTimeout: VaultTimeout,
|
||||
vaultTimeoutAction: VaultTimeoutAction,
|
||||
) => Promise<void>;
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Get the available vault timeout actions for the current user
|
||||
@@ -25,13 +23,13 @@ export abstract class VaultTimeoutSettingsService {
|
||||
* **NOTE:** This observable is not yet connected to the state service, so it will not update when the state changes
|
||||
* @param userId The user id to check. If not provided, the current user is used
|
||||
*/
|
||||
availableVaultTimeoutActions$: (userId?: string) => Observable<VaultTimeoutAction[]>;
|
||||
abstract availableVaultTimeoutActions$(userId?: string): Observable<VaultTimeoutAction[]>;
|
||||
|
||||
/**
|
||||
* Evaluates the user's available vault timeout actions and returns a boolean representing
|
||||
* if the user can lock or not
|
||||
*/
|
||||
canLock: (userId: string) => Promise<boolean>;
|
||||
abstract canLock(userId: string): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Gets the vault timeout action for the given user id. The returned value is
|
||||
@@ -41,7 +39,7 @@ export abstract class VaultTimeoutSettingsService {
|
||||
* A new action will be emitted if the current state changes or if the user's policy changes and the new policy affects the action.
|
||||
* @param userId - the user id to get the vault timeout action for
|
||||
*/
|
||||
getVaultTimeoutActionByUserId$: (userId: string) => Observable<VaultTimeoutAction>;
|
||||
abstract getVaultTimeoutActionByUserId$(userId: string): Observable<VaultTimeoutAction>;
|
||||
|
||||
/**
|
||||
* Get the vault timeout for the given user id. The returned value is calculated based on the current state
|
||||
@@ -50,14 +48,14 @@ export abstract class VaultTimeoutSettingsService {
|
||||
* A new timeout will be emitted if the current state changes or if the user's policy changes and the new policy affects the timeout.
|
||||
* @param userId The user id to get the vault timeout for
|
||||
*/
|
||||
getVaultTimeoutByUserId$: (userId: string) => Observable<VaultTimeout>;
|
||||
abstract getVaultTimeoutByUserId$(userId: string): Observable<VaultTimeout>;
|
||||
|
||||
/**
|
||||
* Has the user enabled unlock with Biometric.
|
||||
* @param userId The user id to check. If not provided, the current user is used
|
||||
* @returns boolean true if biometric lock is set
|
||||
*/
|
||||
isBiometricLockSet: (userId?: string) => Promise<boolean>;
|
||||
abstract isBiometricLockSet(userId?: string): Promise<boolean>;
|
||||
|
||||
clear: (userId: UserId) => Promise<void>;
|
||||
abstract clear(userId: UserId): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
export abstract class VaultTimeoutService {
|
||||
checkVaultTimeout: () => Promise<void>;
|
||||
lock: (userId?: string) => Promise<void>;
|
||||
logOut: (userId?: string) => Promise<void>;
|
||||
abstract checkVaultTimeout(): Promise<void>;
|
||||
abstract lock(userId?: string): Promise<void>;
|
||||
abstract logOut(userId?: string): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ export class CipherExport {
|
||||
view.notes = req.notes;
|
||||
view.favorite = req.favorite;
|
||||
view.reprompt = req.reprompt ?? CipherRepromptType.None;
|
||||
view.key = req.key != null ? new EncString(req.key) : null;
|
||||
|
||||
if (req.fields != null) {
|
||||
view.fields = req.fields.map((f) => FieldExport.toView(f));
|
||||
@@ -80,9 +81,9 @@ export class CipherExport {
|
||||
view.passwordHistory = req.passwordHistory.map((ph) => PasswordHistoryExport.toView(ph));
|
||||
}
|
||||
|
||||
view.creationDate = req.creationDate;
|
||||
view.revisionDate = req.revisionDate;
|
||||
view.deletedDate = req.deletedDate;
|
||||
view.creationDate = req.creationDate ? new Date(req.creationDate) : null;
|
||||
view.revisionDate = req.revisionDate ? new Date(req.revisionDate) : null;
|
||||
view.deletedDate = req.deletedDate ? new Date(req.deletedDate) : null;
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export class PasswordHistoryExport {
|
||||
|
||||
static toView(req: PasswordHistoryExport, view = new PasswordHistoryView()) {
|
||||
view.password = req.password;
|
||||
view.lastUsedDate = req.lastUsedDate;
|
||||
view.lastUsedDate = req.lastUsedDate ? new Date(req.lastUsedDate) : null;
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable, Subject } from "rxjs";
|
||||
|
||||
import { Fido2CredentialView } from "../../../vault/models/view/fido2-credential.view";
|
||||
@@ -25,13 +23,13 @@ export interface ActiveRequest {
|
||||
export type RequestCollection = Readonly<{ [tabId: number]: ActiveRequest }>;
|
||||
|
||||
export abstract class Fido2ActiveRequestManager {
|
||||
getActiveRequest$: (tabId: number) => Observable<ActiveRequest | undefined>;
|
||||
getActiveRequest: (tabId: number) => ActiveRequest | undefined;
|
||||
newActiveRequest: (
|
||||
abstract getActiveRequest$(tabId: number): Observable<ActiveRequest | undefined>;
|
||||
abstract getActiveRequest(tabId: number): ActiveRequest | undefined;
|
||||
abstract newActiveRequest(
|
||||
tabId: number,
|
||||
credentials: Fido2CredentialView[],
|
||||
abortController: AbortController,
|
||||
) => Promise<RequestResult>;
|
||||
removeActiveRequest: (tabId: number) => void;
|
||||
removeAllActiveRequests: () => void;
|
||||
): Promise<RequestResult>;
|
||||
abstract removeActiveRequest(tabId: number): void;
|
||||
abstract removeAllActiveRequests(): void;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Fido2CredentialView } from "../../../vault/models/view/fido2-credential.view";
|
||||
|
||||
/**
|
||||
@@ -17,11 +15,11 @@ export abstract class Fido2AuthenticatorService<ParentWindowReference> {
|
||||
* @param abortController An AbortController that can be used to abort the operation.
|
||||
* @returns A promise that resolves with the new credential and an attestation signature.
|
||||
**/
|
||||
makeCredential: (
|
||||
abstract makeCredential(
|
||||
params: Fido2AuthenticatorMakeCredentialsParams,
|
||||
window: ParentWindowReference,
|
||||
abortController?: AbortController,
|
||||
) => Promise<Fido2AuthenticatorMakeCredentialResult>;
|
||||
): Promise<Fido2AuthenticatorMakeCredentialResult>;
|
||||
|
||||
/**
|
||||
* Generate an assertion using an existing credential as describe in:
|
||||
@@ -31,11 +29,11 @@ export abstract class Fido2AuthenticatorService<ParentWindowReference> {
|
||||
* @param abortController An AbortController that can be used to abort the operation.
|
||||
* @returns A promise that resolves with the asserted credential and an assertion signature.
|
||||
*/
|
||||
getAssertion: (
|
||||
abstract getAssertion(
|
||||
params: Fido2AuthenticatorGetAssertionParams,
|
||||
window: ParentWindowReference,
|
||||
abortController?: AbortController,
|
||||
) => Promise<Fido2AuthenticatorGetAssertionResult>;
|
||||
): Promise<Fido2AuthenticatorGetAssertionResult>;
|
||||
|
||||
/**
|
||||
* Discover credentials for a given Relying Party
|
||||
@@ -43,7 +41,7 @@ export abstract class Fido2AuthenticatorService<ParentWindowReference> {
|
||||
* @param rpId The Relying Party's ID
|
||||
* @returns A promise that resolves with an array of discoverable credentials
|
||||
*/
|
||||
silentCredentialDiscovery: (rpId: string) => Promise<Fido2CredentialView[]>;
|
||||
abstract silentCredentialDiscovery(rpId: string): Promise<Fido2CredentialView[]>;
|
||||
}
|
||||
|
||||
// FIXME: update to use a const object instead of a typescript enum
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
export const UserRequestedFallbackAbortReason = "UserRequestedFallback";
|
||||
|
||||
export type UserVerification = "discouraged" | "preferred" | "required";
|
||||
@@ -16,7 +14,7 @@ export type UserVerification = "discouraged" | "preferred" | "required";
|
||||
* and for returning the results of the latter operations to the Web Authentication API's callers.
|
||||
*/
|
||||
export abstract class Fido2ClientService<ParentWindowReference> {
|
||||
isFido2FeatureEnabled: (hostname: string, origin: string) => Promise<boolean>;
|
||||
abstract isFido2FeatureEnabled(hostname: string, origin: string): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Allows WebAuthn Relying Party scripts to request the creation of a new public key credential source.
|
||||
@@ -26,11 +24,11 @@ export abstract class Fido2ClientService<ParentWindowReference> {
|
||||
* @param abortController An AbortController that can be used to abort the operation.
|
||||
* @returns A promise that resolves with the new credential.
|
||||
*/
|
||||
createCredential: (
|
||||
abstract createCredential(
|
||||
params: CreateCredentialParams,
|
||||
window: ParentWindowReference,
|
||||
abortController?: AbortController,
|
||||
) => Promise<CreateCredentialResult>;
|
||||
): Promise<CreateCredentialResult>;
|
||||
|
||||
/**
|
||||
* Allows WebAuthn Relying Party scripts to discover and use an existing public key credential, with the user’s consent.
|
||||
@@ -41,11 +39,11 @@ export abstract class Fido2ClientService<ParentWindowReference> {
|
||||
* @param abortController An AbortController that can be used to abort the operation.
|
||||
* @returns A promise that resolves with the asserted credential.
|
||||
*/
|
||||
assertCredential: (
|
||||
abstract assertCredential(
|
||||
params: AssertCredentialParams,
|
||||
window: ParentWindowReference,
|
||||
abortController?: AbortController,
|
||||
) => Promise<AssertCredentialResult>;
|
||||
): Promise<AssertCredentialResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
/**
|
||||
* Parameters used to ask the user to confirm the creation of a new credential.
|
||||
*/
|
||||
@@ -69,11 +67,11 @@ export abstract class Fido2UserInterfaceService<ParentWindowReference> {
|
||||
* @param fallbackSupported Whether or not the browser natively supports WebAuthn.
|
||||
* @param abortController An abort controller that can be used to cancel/close the session.
|
||||
*/
|
||||
newSession: (
|
||||
abstract newSession(
|
||||
fallbackSupported: boolean,
|
||||
window: ParentWindowReference,
|
||||
abortController?: AbortController,
|
||||
) => Promise<Fido2UserInterfaceSession>;
|
||||
): Promise<Fido2UserInterfaceSession>;
|
||||
}
|
||||
|
||||
export abstract class Fido2UserInterfaceSession {
|
||||
@@ -84,9 +82,9 @@ export abstract class Fido2UserInterfaceSession {
|
||||
* @param abortController An abort controller that can be used to cancel/close the session.
|
||||
* @returns The ID of the cipher that contains the credentials the user picked. If not cipher was picked, return cipherId = undefined to to let the authenticator throw the error.
|
||||
*/
|
||||
pickCredential: (
|
||||
abstract pickCredential(
|
||||
params: PickCredentialParams,
|
||||
) => Promise<{ cipherId: string; userVerified: boolean }>;
|
||||
): Promise<{ cipherId: string; userVerified: boolean }>;
|
||||
|
||||
/**
|
||||
* Ask the user to confirm the creation of a new credential.
|
||||
@@ -95,30 +93,30 @@ export abstract class Fido2UserInterfaceSession {
|
||||
* @param abortController An abort controller that can be used to cancel/close the session.
|
||||
* @returns The ID of the cipher where the new credential should be saved.
|
||||
*/
|
||||
confirmNewCredential: (
|
||||
abstract confirmNewCredential(
|
||||
params: NewCredentialParams,
|
||||
) => Promise<{ cipherId: string; userVerified: boolean }>;
|
||||
): Promise<{ cipherId: string; userVerified: boolean }>;
|
||||
|
||||
/**
|
||||
* Make sure that the vault is unlocked.
|
||||
* This will open a window and ask the user to login or unlock the vault if necessary.
|
||||
*/
|
||||
ensureUnlockedVault: () => Promise<void>;
|
||||
abstract ensureUnlockedVault(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Inform the user that the operation was cancelled because their vault contains excluded credentials.
|
||||
*
|
||||
* @param existingCipherIds The IDs of the excluded credentials.
|
||||
*/
|
||||
informExcludedCredential: (existingCipherIds: string[]) => Promise<void>;
|
||||
abstract informExcludedCredential(existingCipherIds: string[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Inform the user that the operation was cancelled because their vault does not contain any useable credentials.
|
||||
*/
|
||||
informCredentialNotFound: (abortController?: AbortController) => Promise<void>;
|
||||
abstract informCredentialNotFound(abortController?: AbortController): Promise<void>;
|
||||
|
||||
/**
|
||||
* Close the session, including any windows that may be open.
|
||||
*/
|
||||
close: () => void;
|
||||
abstract close(): void;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// Export the new message sender as the legacy MessagingService to minimize changes in the initial PR,
|
||||
// team specific PR's will come after.
|
||||
export { MessageSender as MessagingService } from "../messaging/message.sender";
|
||||
export { MessageSender as MessagingService } from "@bitwarden/messaging";
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { BiometricKey } from "../../auth/types/biometric-key";
|
||||
import { Account } from "../models/domain/account";
|
||||
import { StorageOptions } from "../models/domain/storage-options";
|
||||
@@ -19,47 +17,47 @@ export type InitOptions = {
|
||||
};
|
||||
|
||||
export abstract class StateService<T extends Account = Account> {
|
||||
addAccount: (account: T) => Promise<void>;
|
||||
clean: (options?: StorageOptions) => Promise<void>;
|
||||
init: (initOptions?: InitOptions) => Promise<void>;
|
||||
abstract addAccount(account: T): Promise<void>;
|
||||
abstract clean(options?: StorageOptions): Promise<void>;
|
||||
abstract init(initOptions?: InitOptions): Promise<void>;
|
||||
|
||||
/**
|
||||
* Gets the user's auto key
|
||||
*/
|
||||
getUserKeyAutoUnlock: (options?: StorageOptions) => Promise<string>;
|
||||
abstract getUserKeyAutoUnlock(options?: StorageOptions): Promise<string>;
|
||||
/**
|
||||
* Sets the user's auto key
|
||||
*/
|
||||
setUserKeyAutoUnlock: (value: string | null, options?: StorageOptions) => Promise<void>;
|
||||
abstract setUserKeyAutoUnlock(value: string | null, options?: StorageOptions): Promise<void>;
|
||||
/**
|
||||
* Gets the user's biometric key
|
||||
*/
|
||||
getUserKeyBiometric: (options?: StorageOptions) => Promise<string>;
|
||||
abstract getUserKeyBiometric(options?: StorageOptions): Promise<string>;
|
||||
/**
|
||||
* Checks if the user has a biometric key available
|
||||
*/
|
||||
hasUserKeyBiometric: (options?: StorageOptions) => Promise<boolean>;
|
||||
abstract hasUserKeyBiometric(options?: StorageOptions): Promise<boolean>;
|
||||
/**
|
||||
* Sets the user's biometric key
|
||||
*/
|
||||
setUserKeyBiometric: (value: BiometricKey, options?: StorageOptions) => Promise<void>;
|
||||
abstract setUserKeyBiometric(value: BiometricKey, options?: StorageOptions): Promise<void>;
|
||||
/**
|
||||
* @deprecated For backwards compatible purposes only, use DesktopAutofillSettingsService
|
||||
*/
|
||||
setEnableDuckDuckGoBrowserIntegration: (
|
||||
abstract setEnableDuckDuckGoBrowserIntegration(
|
||||
value: boolean,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getDuckDuckGoSharedKey: (options?: StorageOptions) => Promise<string>;
|
||||
setDuckDuckGoSharedKey: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
): Promise<void>;
|
||||
abstract getDuckDuckGoSharedKey(options?: StorageOptions): Promise<string>;
|
||||
abstract setDuckDuckGoSharedKey(value: string, options?: StorageOptions): Promise<void>;
|
||||
|
||||
/**
|
||||
* @deprecated Use `TokenService.hasAccessToken$()` or `AuthService.authStatusFor$` instead.
|
||||
*/
|
||||
getIsAuthenticated: (options?: StorageOptions) => Promise<boolean>;
|
||||
abstract getIsAuthenticated(options?: StorageOptions): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* @deprecated Use `AccountService.activeAccount$` instead.
|
||||
*/
|
||||
getUserId: (options?: StorageOptions) => Promise<string>;
|
||||
abstract getUserId(options?: StorageOptions): Promise<string>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
export { MessageListener } from "./message.listener";
|
||||
export { MessageSender } from "./message.sender";
|
||||
export { Message, CommandDefinition } from "./types";
|
||||
export { isExternalMessage } from "./helpers";
|
||||
export * from "@bitwarden/messaging";
|
||||
|
||||
@@ -1,5 +1 @@
|
||||
// Built in implementations
|
||||
export { SubjectMessageSender } from "./subject-message.sender";
|
||||
|
||||
// Helpers meant to be used only by other implementations
|
||||
export { tagAsExternal, getCommand } from "./helpers";
|
||||
export * from "@bitwarden/messaging-internal";
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import { Subject } from "rxjs";
|
||||
|
||||
import { subscribeTo } from "../../../spec/observable-tracker";
|
||||
|
||||
import { SubjectMessageSender } from "./internal";
|
||||
import { MessageSender } from "./message.sender";
|
||||
import { Message, CommandDefinition } from "./types";
|
||||
|
||||
describe("SubjectMessageSender", () => {
|
||||
const subject = new Subject<Message<{ test: number }>>();
|
||||
const subjectObservable = subject.asObservable();
|
||||
|
||||
const sut: MessageSender = new SubjectMessageSender(subject);
|
||||
|
||||
describe("send", () => {
|
||||
it("will send message with command from message definition", async () => {
|
||||
const commandDefinition = new CommandDefinition<{ test: number }>("myCommand");
|
||||
|
||||
const tracker = subscribeTo(subjectObservable);
|
||||
const pausePromise = tracker.pauseUntilReceived(1);
|
||||
|
||||
sut.send(commandDefinition, { test: 1 });
|
||||
|
||||
await pausePromise;
|
||||
|
||||
expect(tracker.emissions[0]).toEqual({ command: "myCommand", test: 1 });
|
||||
});
|
||||
|
||||
it("will send message with command from normal string", async () => {
|
||||
const tracker = subscribeTo(subjectObservable);
|
||||
const pausePromise = tracker.pauseUntilReceived(1);
|
||||
|
||||
sut.send("myCommand", { test: 1 });
|
||||
|
||||
await pausePromise;
|
||||
|
||||
expect(tracker.emissions[0]).toEqual({ command: "myCommand", test: 1 });
|
||||
});
|
||||
|
||||
it("will send message with object even if payload not given", async () => {
|
||||
const tracker = subscribeTo(subjectObservable);
|
||||
const pausePromise = tracker.pauseUntilReceived(1);
|
||||
|
||||
sut.send("myCommand");
|
||||
|
||||
await pausePromise;
|
||||
|
||||
expect(tracker.emissions[0]).toEqual({ command: "myCommand" });
|
||||
});
|
||||
|
||||
it.each([null, undefined])(
|
||||
"will send message with object even if payload is null-ish (%s)",
|
||||
async (payloadValue) => {
|
||||
const tracker = subscribeTo(subjectObservable);
|
||||
const pausePromise = tracker.pauseUntilReceived(1);
|
||||
|
||||
sut.send("myCommand", payloadValue);
|
||||
|
||||
await pausePromise;
|
||||
|
||||
expect(tracker.emissions[0]).toEqual({ command: "myCommand" });
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,9 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { ZXCVBNResult } from "zxcvbn";
|
||||
|
||||
export abstract class PasswordStrengthServiceAbstraction {
|
||||
getPasswordStrength: (password: string, email?: string, userInputs?: string[]) => ZXCVBNResult;
|
||||
abstract getPasswordStrength(
|
||||
password: string,
|
||||
email?: string,
|
||||
userInputs?: string[],
|
||||
): ZXCVBNResult;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export class SendData {
|
||||
expirationDate: string;
|
||||
deletionDate: string;
|
||||
password: string;
|
||||
emails: string;
|
||||
disabled: boolean;
|
||||
hideEmail: boolean;
|
||||
|
||||
@@ -41,6 +42,7 @@ export class SendData {
|
||||
this.expirationDate = response.expirationDate;
|
||||
this.deletionDate = response.deletionDate;
|
||||
this.password = response.password;
|
||||
this.emails = response.emails;
|
||||
this.disabled = response.disable;
|
||||
this.hideEmail = response.hideEmail;
|
||||
|
||||
|
||||
@@ -29,14 +29,15 @@ describe("Send", () => {
|
||||
text: "encText",
|
||||
hidden: true,
|
||||
},
|
||||
file: null,
|
||||
file: null!,
|
||||
key: "encKey",
|
||||
maxAccessCount: null,
|
||||
maxAccessCount: null!,
|
||||
accessCount: 10,
|
||||
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||
expirationDate: "2022-01-31T12:00:00.000Z",
|
||||
deletionDate: "2022-01-31T12:00:00.000Z",
|
||||
password: "password",
|
||||
emails: null!,
|
||||
disabled: false,
|
||||
hideEmail: true,
|
||||
};
|
||||
@@ -86,6 +87,7 @@ describe("Send", () => {
|
||||
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||
deletionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||
password: "password",
|
||||
emails: null!,
|
||||
disabled: false,
|
||||
hideEmail: true,
|
||||
});
|
||||
|
||||
@@ -27,6 +27,7 @@ export class Send extends Domain {
|
||||
expirationDate: Date;
|
||||
deletionDate: Date;
|
||||
password: string;
|
||||
emails: string;
|
||||
disabled: boolean;
|
||||
hideEmail: boolean;
|
||||
|
||||
@@ -53,6 +54,7 @@ export class Send extends Domain {
|
||||
this.maxAccessCount = obj.maxAccessCount;
|
||||
this.accessCount = obj.accessCount;
|
||||
this.password = obj.password;
|
||||
this.emails = obj.emails;
|
||||
this.disabled = obj.disabled;
|
||||
this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null;
|
||||
this.deletionDate = obj.deletionDate != null ? new Date(obj.deletionDate) : null;
|
||||
|
||||
@@ -17,6 +17,7 @@ export class SendRequest {
|
||||
text: SendTextApi;
|
||||
file: SendFileApi;
|
||||
password: string;
|
||||
emails: string;
|
||||
disabled: boolean;
|
||||
hideEmail: boolean;
|
||||
|
||||
@@ -30,6 +31,7 @@ export class SendRequest {
|
||||
this.deletionDate = send.deletionDate != null ? send.deletionDate.toISOString() : null;
|
||||
this.key = send.key != null ? send.key.encryptedString : null;
|
||||
this.password = send.password;
|
||||
this.emails = send.emails;
|
||||
this.disabled = send.disabled;
|
||||
this.hideEmail = send.hideEmail;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ export class SendResponse extends BaseResponse {
|
||||
expirationDate: string;
|
||||
deletionDate: string;
|
||||
password: string;
|
||||
emails: string;
|
||||
disable: boolean;
|
||||
hideEmail: boolean;
|
||||
|
||||
@@ -37,6 +38,7 @@ export class SendResponse extends BaseResponse {
|
||||
this.expirationDate = this.getResponseProperty("ExpirationDate");
|
||||
this.deletionDate = this.getResponseProperty("DeletionDate");
|
||||
this.password = this.getResponseProperty("Password");
|
||||
this.emails = this.getResponseProperty("Emails");
|
||||
this.disable = this.getResponseProperty("Disabled") || false;
|
||||
this.hideEmail = this.getResponseProperty("HideEmail") || false;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ export class SendView implements View {
|
||||
deletionDate: Date = null;
|
||||
expirationDate: Date = null;
|
||||
password: string = null;
|
||||
emails: string[] = [];
|
||||
disabled = false;
|
||||
hideEmail = false;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { ListResponse } from "../../../models/response/list.response";
|
||||
import { EncArrayBuffer } from "../../../platform/models/domain/enc-array-buffer";
|
||||
import { Send } from "../models/domain/send";
|
||||
@@ -12,26 +10,29 @@ import { SendResponse } from "../models/response/send.response";
|
||||
import { SendAccessView } from "../models/view/send-access.view";
|
||||
|
||||
export abstract class SendApiService {
|
||||
getSend: (id: string) => Promise<SendResponse>;
|
||||
postSendAccess: (
|
||||
abstract getSend(id: string): Promise<SendResponse>;
|
||||
abstract postSendAccess(
|
||||
id: string,
|
||||
request: SendAccessRequest,
|
||||
apiUrl?: string,
|
||||
) => Promise<SendAccessResponse>;
|
||||
getSends: () => Promise<ListResponse<SendResponse>>;
|
||||
postSend: (request: SendRequest) => Promise<SendResponse>;
|
||||
postFileTypeSend: (request: SendRequest) => Promise<SendFileUploadDataResponse>;
|
||||
postSendFile: (sendId: string, fileId: string, data: FormData) => Promise<any>;
|
||||
putSend: (id: string, request: SendRequest) => Promise<SendResponse>;
|
||||
putSendRemovePassword: (id: string) => Promise<SendResponse>;
|
||||
deleteSend: (id: string) => Promise<any>;
|
||||
getSendFileDownloadData: (
|
||||
): Promise<SendAccessResponse>;
|
||||
abstract getSends(): Promise<ListResponse<SendResponse>>;
|
||||
abstract postSend(request: SendRequest): Promise<SendResponse>;
|
||||
abstract postFileTypeSend(request: SendRequest): Promise<SendFileUploadDataResponse>;
|
||||
abstract postSendFile(sendId: string, fileId: string, data: FormData): Promise<any>;
|
||||
abstract putSend(id: string, request: SendRequest): Promise<SendResponse>;
|
||||
abstract putSendRemovePassword(id: string): Promise<SendResponse>;
|
||||
abstract deleteSend(id: string): Promise<any>;
|
||||
abstract getSendFileDownloadData(
|
||||
send: SendAccessView,
|
||||
request: SendAccessRequest,
|
||||
apiUrl?: string,
|
||||
) => Promise<SendFileDownloadDataResponse>;
|
||||
renewSendFileUploadUrl: (sendId: string, fileId: string) => Promise<SendFileUploadDataResponse>;
|
||||
removePassword: (id: string) => Promise<any>;
|
||||
delete: (id: string) => Promise<any>;
|
||||
save: (sendData: [Send, EncArrayBuffer]) => Promise<Send>;
|
||||
): Promise<SendFileDownloadDataResponse>;
|
||||
abstract renewSendFileUploadUrl(
|
||||
sendId: string,
|
||||
fileId: string,
|
||||
): Promise<SendFileUploadDataResponse>;
|
||||
abstract removePassword(id: string): Promise<any>;
|
||||
abstract delete(id: string): Promise<any>;
|
||||
abstract save(sendData: [Send, EncArrayBuffer]): Promise<Send>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
@@ -16,49 +14,49 @@ import { SendWithIdRequest } from "../models/request/send-with-id.request";
|
||||
import { SendView } from "../models/view/send.view";
|
||||
|
||||
export abstract class SendService implements UserKeyRotationDataProvider<SendWithIdRequest> {
|
||||
sends$: Observable<Send[]>;
|
||||
sendViews$: Observable<SendView[]>;
|
||||
abstract sends$: Observable<Send[]>;
|
||||
abstract sendViews$: Observable<SendView[]>;
|
||||
|
||||
encrypt: (
|
||||
abstract encrypt(
|
||||
model: SendView,
|
||||
file: File | ArrayBuffer,
|
||||
password: string,
|
||||
key?: SymmetricCryptoKey,
|
||||
) => Promise<[Send, EncArrayBuffer]>;
|
||||
): Promise<[Send, EncArrayBuffer]>;
|
||||
/**
|
||||
* Provides a send for a determined id
|
||||
* updates after a change occurs to the send that matches the id
|
||||
* @param id The id of the desired send
|
||||
* @returns An observable that listens to the value of the desired send
|
||||
*/
|
||||
get$: (id: string) => Observable<Send | undefined>;
|
||||
abstract get$(id: string): Observable<Send | undefined>;
|
||||
/**
|
||||
* Provides re-encrypted user sends for the key rotation process
|
||||
* @param newUserKey The new user key to use for re-encryption
|
||||
* @throws Error if the new user key is null or undefined
|
||||
* @returns A list of user sends that have been re-encrypted with the new user key
|
||||
*/
|
||||
getRotatedData: (
|
||||
abstract getRotatedData(
|
||||
originalUserKey: UserKey,
|
||||
newUserKey: UserKey,
|
||||
userId: UserId,
|
||||
) => Promise<SendWithIdRequest[]>;
|
||||
): Promise<SendWithIdRequest[]>;
|
||||
/**
|
||||
* @deprecated Do not call this, use the sends$ observable collection
|
||||
*/
|
||||
getAll: () => Promise<Send[]>;
|
||||
abstract getAll(): Promise<Send[]>;
|
||||
/**
|
||||
* @deprecated Only use in CLI
|
||||
*/
|
||||
getFromState: (id: string) => Promise<Send>;
|
||||
abstract getFromState(id: string): Promise<Send>;
|
||||
/**
|
||||
* @deprecated Only use in CLI
|
||||
*/
|
||||
getAllDecryptedFromState: (userId: UserId) => Promise<SendView[]>;
|
||||
abstract getAllDecryptedFromState(userId: UserId): Promise<SendView[]>;
|
||||
}
|
||||
|
||||
export abstract class InternalSendService extends SendService {
|
||||
upsert: (send: SendData | SendData[]) => Promise<any>;
|
||||
replace: (sends: { [id: string]: SendData }, userId: UserId) => Promise<void>;
|
||||
delete: (id: string | string[]) => Promise<any>;
|
||||
abstract upsert(send: SendData | SendData[]): Promise<any>;
|
||||
abstract replace(sends: { [id: string]: SendData }, userId: UserId): Promise<void>;
|
||||
abstract delete(id: string | string[]): Promise<any>;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,12 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
model.key = key.material;
|
||||
model.cryptoKey = key.derivedKey;
|
||||
}
|
||||
if (password != null) {
|
||||
|
||||
const hasEmails = (model.emails?.length ?? 0) > 0;
|
||||
if (hasEmails) {
|
||||
send.emails = model.emails.join(",");
|
||||
send.password = null;
|
||||
} else if (password != null) {
|
||||
// Note: Despite being called key, the passwordKey is not used for encryption.
|
||||
// It is used as a static proof that the client knows the password, and has the encryption key.
|
||||
const passwordKey = await this.keyGenerationService.deriveKeyFromPassword(
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { EncString } from "../../../key-management/crypto/models/enc-string";
|
||||
import { EncArrayBuffer } from "../../../platform/models/domain/enc-array-buffer";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
@@ -7,11 +5,11 @@ import { Cipher } from "../../models/domain/cipher";
|
||||
import { CipherResponse } from "../../models/response/cipher.response";
|
||||
|
||||
export abstract class CipherFileUploadService {
|
||||
upload: (
|
||||
abstract upload(
|
||||
cipher: Cipher,
|
||||
encFileName: EncString,
|
||||
encData: EncArrayBuffer,
|
||||
admin: boolean,
|
||||
dataEncKey: [SymmetricCryptoKey, EncString],
|
||||
) => Promise<CipherResponse>;
|
||||
): Promise<CipherResponse>;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { FolderData } from "../../models/data/folder.data";
|
||||
import { Folder } from "../../models/domain/folder";
|
||||
import { FolderResponse } from "../../models/response/folder.response";
|
||||
|
||||
export class FolderApiServiceAbstraction {
|
||||
save: (folder: Folder, userId: UserId) => Promise<FolderData>;
|
||||
delete: (id: string, userId: UserId) => Promise<any>;
|
||||
get: (id: string) => Promise<FolderResponse>;
|
||||
deleteAll: (userId: UserId) => Promise<void>;
|
||||
export abstract class FolderApiServiceAbstraction {
|
||||
abstract save(folder: Folder, userId: UserId): Promise<FolderData>;
|
||||
abstract delete(id: string, userId: UserId): Promise<any>;
|
||||
abstract get(id: string): Promise<FolderResponse>;
|
||||
abstract deleteAll(userId: UserId): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
@@ -15,27 +13,27 @@ import { FolderWithIdRequest } from "../../models/request/folder-with-id.request
|
||||
import { FolderView } from "../../models/view/folder.view";
|
||||
|
||||
export abstract class FolderService implements UserKeyRotationDataProvider<FolderWithIdRequest> {
|
||||
folders$: (userId: UserId) => Observable<Folder[]>;
|
||||
folderViews$: (userId: UserId) => Observable<FolderView[]>;
|
||||
abstract folders$(userId: UserId): Observable<Folder[]>;
|
||||
abstract folderViews$(userId: UserId): Observable<FolderView[]>;
|
||||
|
||||
clearDecryptedFolderState: (userId: UserId) => Promise<void>;
|
||||
encrypt: (model: FolderView, key: SymmetricCryptoKey) => Promise<Folder>;
|
||||
get: (id: string, userId: UserId) => Promise<Folder>;
|
||||
getDecrypted$: (id: string, userId: UserId) => Observable<FolderView | undefined>;
|
||||
abstract clearDecryptedFolderState(userId: UserId): Promise<void>;
|
||||
abstract encrypt(model: FolderView, key: SymmetricCryptoKey): Promise<Folder>;
|
||||
abstract get(id: string, userId: UserId): Promise<Folder>;
|
||||
abstract getDecrypted$(id: string, userId: UserId): Observable<FolderView | undefined>;
|
||||
/**
|
||||
* @deprecated Use firstValueFrom(folders$) directly instead
|
||||
* @param userId The user id
|
||||
* @returns Promise of folders array
|
||||
*/
|
||||
getAllFromState: (userId: UserId) => Promise<Folder[]>;
|
||||
abstract getAllFromState(userId: UserId): Promise<Folder[]>;
|
||||
/**
|
||||
* @deprecated Only use in CLI!
|
||||
*/
|
||||
getFromState: (id: string, userId: UserId) => Promise<Folder>;
|
||||
abstract getFromState(id: string, userId: UserId): Promise<Folder>;
|
||||
/**
|
||||
* @deprecated Only use in CLI!
|
||||
*/
|
||||
getAllDecryptedFromState: (userId: UserId) => Promise<FolderView[]>;
|
||||
abstract getAllDecryptedFromState(userId: UserId): Promise<FolderView[]>;
|
||||
/**
|
||||
* Returns user folders re-encrypted with the new user key.
|
||||
* @param originalUserKey the original user key
|
||||
@@ -44,16 +42,16 @@ export abstract class FolderService implements UserKeyRotationDataProvider<Folde
|
||||
* @throws Error if new user key is null
|
||||
* @returns a list of user folders that have been re-encrypted with the new user key
|
||||
*/
|
||||
getRotatedData: (
|
||||
abstract getRotatedData(
|
||||
originalUserKey: UserKey,
|
||||
newUserKey: UserKey,
|
||||
userId: UserId,
|
||||
) => Promise<FolderWithIdRequest[]>;
|
||||
): Promise<FolderWithIdRequest[]>;
|
||||
}
|
||||
|
||||
export abstract class InternalFolderService extends FolderService {
|
||||
upsert: (folder: FolderData | FolderData[], userId: UserId) => Promise<void>;
|
||||
replace: (folders: { [id: string]: FolderData }, userId: UserId) => Promise<void>;
|
||||
clear: (userId: UserId) => Promise<void>;
|
||||
delete: (id: string | string[], userId: UserId) => Promise<any>;
|
||||
abstract upsert(folder: FolderData | FolderData[], userId: UserId): Promise<void>;
|
||||
abstract replace(folders: { [id: string]: FolderData }, userId: UserId): Promise<void>;
|
||||
abstract clear(userId: UserId): Promise<void>;
|
||||
abstract delete(id: string | string[], userId: UserId): Promise<any>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { SendView } from "../../tools/send/models/view/send.view";
|
||||
@@ -8,25 +6,25 @@ import { CipherView } from "../models/view/cipher.view";
|
||||
import { CipherViewLike } from "../utils/cipher-view-like-utils";
|
||||
|
||||
export abstract class SearchService {
|
||||
indexedEntityId$: (userId: UserId) => Observable<IndexedEntityId | null>;
|
||||
abstract indexedEntityId$(userId: UserId): Observable<IndexedEntityId | null>;
|
||||
|
||||
clearIndex: (userId: UserId) => Promise<void>;
|
||||
isSearchable: (userId: UserId, query: string) => Promise<boolean>;
|
||||
indexCiphers: (
|
||||
abstract clearIndex(userId: UserId): Promise<void>;
|
||||
abstract isSearchable(userId: UserId, query: string): Promise<boolean>;
|
||||
abstract indexCiphers(
|
||||
userId: UserId,
|
||||
ciphersToIndex: CipherView[],
|
||||
indexedEntityGuid?: string,
|
||||
) => Promise<void>;
|
||||
searchCiphers: <C extends CipherViewLike>(
|
||||
): Promise<void>;
|
||||
abstract searchCiphers<C extends CipherViewLike>(
|
||||
userId: UserId,
|
||||
query: string,
|
||||
filter?: ((cipher: C) => boolean) | ((cipher: C) => boolean)[],
|
||||
ciphers?: C[],
|
||||
) => Promise<C[]>;
|
||||
searchCiphersBasic: <C extends CipherViewLike>(
|
||||
): Promise<C[]>;
|
||||
abstract searchCiphersBasic<C extends CipherViewLike>(
|
||||
ciphers: C[],
|
||||
query: string,
|
||||
deleted?: boolean,
|
||||
) => C[];
|
||||
searchSends: (sends: SendView[], query: string) => SendView[];
|
||||
): C[];
|
||||
abstract searchSends(sends: SendView[], query: string): SendView[];
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
/**
|
||||
* Service for managing vault settings.
|
||||
@@ -9,42 +7,40 @@ export abstract class VaultSettingsService {
|
||||
* An observable monitoring the state of the enable passkeys setting.
|
||||
* The observable updates when the setting changes.
|
||||
*/
|
||||
enablePasskeys$: Observable<boolean>;
|
||||
abstract enablePasskeys$: Observable<boolean>;
|
||||
/**
|
||||
* An observable monitoring the state of the show cards on the current tab.
|
||||
*/
|
||||
showCardsCurrentTab$: Observable<boolean>;
|
||||
abstract showCardsCurrentTab$: Observable<boolean>;
|
||||
/**
|
||||
* An observable monitoring the state of the show identities on the current tab.
|
||||
*/
|
||||
showIdentitiesCurrentTab$: Observable<boolean>;
|
||||
/**
|
||||
abstract showIdentitiesCurrentTab$: Observable<boolean>;
|
||||
/**
|
||||
* An observable monitoring the state of the click items on the Vault view
|
||||
* for Autofill suggestions.
|
||||
*/
|
||||
clickItemsToAutofillVaultView$: Observable<boolean>;
|
||||
/**
|
||||
abstract clickItemsToAutofillVaultView$: Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Saves the enable passkeys setting to disk.
|
||||
* @param value The new value for the passkeys setting.
|
||||
*/
|
||||
setEnablePasskeys: (value: boolean) => Promise<void>;
|
||||
abstract setEnablePasskeys(value: boolean): Promise<void>;
|
||||
/**
|
||||
* Saves the show cards on tab page setting to disk.
|
||||
* @param value The new value for the show cards on tab page setting.
|
||||
*/
|
||||
setShowCardsCurrentTab: (value: boolean) => Promise<void>;
|
||||
abstract setShowCardsCurrentTab(value: boolean): Promise<void>;
|
||||
/**
|
||||
* Saves the show identities on tab page setting to disk.
|
||||
* @param value The new value for the show identities on tab page setting.
|
||||
*/
|
||||
setShowIdentitiesCurrentTab: (value: boolean) => Promise<void>;
|
||||
abstract setShowIdentitiesCurrentTab(value: boolean): Promise<void>;
|
||||
/**
|
||||
* Saves the click items on vault View for Autofill suggestions to disk.
|
||||
* @param value The new value for the click items on vault View for
|
||||
* Autofill suggestions setting.
|
||||
*/
|
||||
setClickItemsToAutofillVaultView: (value: boolean) => Promise<void>;
|
||||
abstract setClickItemsToAutofillVaultView(value: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -386,11 +386,12 @@ export abstract class KeyService {
|
||||
/**
|
||||
* Initialize all necessary crypto keys needed for a new account.
|
||||
* Warning! This completely replaces any existing keys!
|
||||
* @param userId The user id of the target user.
|
||||
* @returns The user's newly created public key, private key, and encrypted private key
|
||||
*
|
||||
* @throws An error if there is no user currently active.
|
||||
* @throws An error if the userId is null or undefined.
|
||||
* @throws An error if the user already has a user key.
|
||||
*/
|
||||
abstract initAccount(): Promise<{
|
||||
abstract initAccount(userId: UserId): Promise<{
|
||||
userKey: UserKey;
|
||||
publicKey: string;
|
||||
privateKey: EncString;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user