Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { Injectable } from "@angular/core";
import { BehaviorSubject, firstValueFrom, map } from "rxjs";

import {
OrganizationUserApiService,
OrganizationUserAcceptRequest,
OrganizationUserAcceptInitRequest,
OrganizationUserAcceptRequest,
OrganizationUserApiService,
} from "@bitwarden/admin-console/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
Expand Down Expand Up @@ -105,24 +105,19 @@ export class AcceptOrganizationInviteService {
invite: OrganizationInvite,
activeUserId: UserId,
): Promise<OrganizationUserAcceptInitRequest> {
const request = new OrganizationUserAcceptInitRequest();
request.token = invite.token;

const [encryptedOrgKey, orgKey] = await this.keyService.makeOrgKey<OrgKey>(activeUserId);
const [orgPublicKey, encryptedOrgPrivateKey] = await this.keyService.makeKeyPair(orgKey);
const collection = await this.encryptService.encryptString(
this.i18nService.t("defaultCollection"),
orgKey,
);

request.key = encryptedOrgKey.encryptedString;
request.keys = new OrganizationKeysRequest(
orgPublicKey,
encryptedOrgPrivateKey.encryptedString,
return new OrganizationUserAcceptInitRequest(
invite.token,
encryptedOrgKey.encryptedString,
new OrganizationKeysRequest(orgPublicKey, encryptedOrgPrivateKey.encryptedString),
collection.encryptedString,
);
request.collectionName = collection.encryptedString;

return request;
}

private async accept(invite: OrganizationInvite): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1016,16 +1016,17 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
orgKey: SymmetricCryptoKey;
activeUserId: UserId;
}): Promise<string> {
const request = new OrganizationCreateRequest();
request.key = encryptionData.key;
request.collectionName = encryptionData.collectionCt;
const request = new OrganizationCreateRequest(
encryptionData.key,
new OrganizationKeysRequest(
encryptionData.orgKeys[0],
encryptionData.orgKeys[1].encryptedString as string,
),
encryptionData.collectionCt,
);
request.name = this.formGroup.controls.name.value ?? "";
request.billingEmail = this.formGroup.controls.billingEmail.value ?? "";
request.initiationPath = "New organization creation in-product";
request.keys = new OrganizationKeysRequest(
encryptionData.orgKeys[0],
encryptionData.orgKeys[1].encryptedString as string,
);

if (this.selectedPlan()!.type === PlanType.Free) {
request.planType = PlanType.Free;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ export class WebProviderService {
providerKey,
);

const request = new CreateProviderOrganizationRequest();
request.name = name;
request.ownerEmail = ownerEmail;
request.planType = planType;
request.seats = seats;

request.key = encryptedProviderKey.encryptedString;
request.keyPair = new OrganizationKeysRequest(publicKey, encryptedPrivateKey.encryptedString);
request.collectionName = encryptedCollectionName.encryptedString;
const request = new CreateProviderOrganizationRequest(
name,
ownerEmail,
planType,
seats,
encryptedProviderKey.encryptedString,
new OrganizationKeysRequest(publicKey, encryptedPrivateKey.encryptedString),
encryptedCollectionName.encryptedString,
);

await this.providerApiService.createProviderOrganization(providerId, request);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";

import { OrganizationUserAcceptInitRequest } from "./organization-user-accept-init.request";

describe("OrganizationUserAcceptInitRequest", () => {
const validToken = "invite-token";
const validKey = "encrypted-org-key";
const validKeys = new OrganizationKeysRequest("public-key", "encrypted-private-key");
const validCollectionName = "encrypted-collection-name";

it("should create a request with all required parameters", () => {
const request = new OrganizationUserAcceptInitRequest(
validToken,
validKey,
validKeys,
validCollectionName,
);

expect(request.token).toBe(validToken);
expect(request.key).toBe(validKey);
expect(request.keys).toBe(validKeys);
expect(request.collectionName).toBe(validCollectionName);
});

it("should throw when token is empty", () => {
expect(
() => new OrganizationUserAcceptInitRequest("", validKey, validKeys, validCollectionName),
).toThrow("Token is required");
});

it("should throw when key is empty", () => {
expect(
() => new OrganizationUserAcceptInitRequest(validToken, "", validKeys, validCollectionName),
).toThrow("Organization key is required");
});

it("should throw when keys is null", () => {
expect(
() => new OrganizationUserAcceptInitRequest(validToken, validKey, null!, validCollectionName),
).toThrow("Organization keys are required");
});

it("should throw when keys is undefined", () => {
expect(
() =>
new OrganizationUserAcceptInitRequest(
validToken,
validKey,
undefined!,
validCollectionName,
),
).toThrow("Organization keys are required");
});

it("should throw when collectionName is empty", () => {
expect(
() => new OrganizationUserAcceptInitRequest(validToken, validKey, validKeys, ""),
).toThrow("Collection name is required");
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";

export class OrganizationUserAcceptInitRequest {
token: string;
key: string;
keys: OrganizationKeysRequest;
collectionName: string;

constructor(token: string, key: string, keys: OrganizationKeysRequest, collectionName: string) {
if (!token) {
throw new Error("Token is required");
}
if (!key) {
throw new Error("Organization key is required");
}
if (!keys) {
throw new Error("Organization keys are required");
}
if (!collectionName) {
throw new Error("Collection name is required");
}
this.token = token;
this.key = key;
this.keys = keys;
this.collectionName = collectionName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { PlanType } from "../../../billing/enums";

import { CreateProviderOrganizationRequest } from "./create-provider-organization.request";
import { OrganizationKeysRequest } from "./organization-keys.request";

describe("CreateProviderOrganizationRequest", () => {
const validName = "Test Org";
const validOwnerEmail = "owner@example.com";
const validPlanType = PlanType.TeamsAnnually;
const validSeats = 10;
const validKey = "encrypted-org-key";
const validKeyPair = new OrganizationKeysRequest("public-key", "encrypted-private-key");
const validCollectionName = "encrypted-collection-name";

const createRequest = (
overrides: Partial<{
name: string;
ownerEmail: string;
planType: PlanType;
seats: number;
key: string;
keyPair: OrganizationKeysRequest;
collectionName: string;
}> = {},
) => {
return new CreateProviderOrganizationRequest(
"name" in overrides ? overrides.name! : validName,
"ownerEmail" in overrides ? overrides.ownerEmail! : validOwnerEmail,
"planType" in overrides ? overrides.planType! : validPlanType,
"seats" in overrides ? overrides.seats! : validSeats,
"key" in overrides ? overrides.key! : validKey,
"keyPair" in overrides ? overrides.keyPair! : validKeyPair,
"collectionName" in overrides ? overrides.collectionName! : validCollectionName,
);
};

it("should create a request with all required parameters", () => {
const request = createRequest();

expect(request.name).toBe(validName);
expect(request.ownerEmail).toBe(validOwnerEmail);
expect(request.planType).toBe(validPlanType);
expect(request.seats).toBe(validSeats);
expect(request.key).toBe(validKey);
expect(request.keyPair).toBe(validKeyPair);
expect(request.collectionName).toBe(validCollectionName);
});

it("should throw when name is empty", () => {
expect(() => createRequest({ name: "" })).toThrow("Name is required");
});

it("should throw when ownerEmail is empty", () => {
expect(() => createRequest({ ownerEmail: "" })).toThrow("Owner email is required");
});

it("should throw when planType is null", () => {
expect(() => createRequest({ planType: null! })).toThrow("Plan type is required");
});

it("should throw when seats is null", () => {
expect(() => createRequest({ seats: null! })).toThrow("Seats is required");
});

it("should throw when key is empty", () => {
expect(() => createRequest({ key: "" })).toThrow("Organization key is required");
});

it("should throw when keyPair is null", () => {
expect(() => createRequest({ keyPair: null! })).toThrow("Organization key pair is required");
});

it("should throw when keyPair is undefined", () => {
expect(() => createRequest({ keyPair: undefined })).toThrow(
"Organization key pair is required",
);
});

it("should throw when collectionName is empty", () => {
expect(() => createRequest({ collectionName: "" })).toThrow("Collection name is required");
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { PlanType } from "../../../billing/enums";

import { OrganizationKeysRequest } from "./organization-keys.request";
Expand All @@ -12,4 +10,43 @@ export class CreateProviderOrganizationRequest {
key: string;
keyPair: OrganizationKeysRequest;
collectionName: string;

constructor(
name: string,
ownerEmail: string,
planType: PlanType,
seats: number,
key: string,
keyPair: OrganizationKeysRequest,
collectionName: string,
) {
if (!name) {
throw new Error("Name is required");
}
if (!ownerEmail) {
throw new Error("Owner email is required");
}
if (planType == null) {
throw new Error("Plan type is required");
}
if (seats == null) {
throw new Error("Seats is required");
}
if (!key) {
throw new Error("Organization key is required");
}
if (!keyPair) {
throw new Error("Organization key pair is required");
}
if (!collectionName) {
throw new Error("Collection name is required");
}
this.name = name;
this.ownerEmail = ownerEmail;
this.planType = planType;
this.seats = seats;
this.key = key;
this.keyPair = keyPair;
this.collectionName = collectionName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { OrganizationCreateRequest } from "./organization-create.request";
import { OrganizationKeysRequest } from "./organization-keys.request";

describe("OrganizationCreateRequest", () => {
const validKey = "encrypted-org-key";
const validKeys = new OrganizationKeysRequest("public-key", "encrypted-private-key");
const validCollectionName = "encrypted-collection-name";

it("should create a request with valid key parameters", () => {
const request = new OrganizationCreateRequest(validKey, validKeys, validCollectionName);

expect(request.key).toBe(validKey);
expect(request.keys).toBe(validKeys);
expect(request.collectionName).toBe(validCollectionName);
});

it("should inherit validation from parent class", () => {
expect(() => new OrganizationCreateRequest("", validKeys, validCollectionName)).toThrow(
"Organization key is required",
);

expect(() => new OrganizationCreateRequest(validKey, null!, validCollectionName)).toThrow(
"Organization keys are required",
);

expect(() => new OrganizationCreateRequest(validKey, validKeys, "")).toThrow(
"Collection name is required",
);
});

it("should allow setting payment fields after construction", () => {
const request = new OrganizationCreateRequest(validKey, validKeys, validCollectionName);
request.paymentToken = "token";

expect(request.paymentToken).toBe("token");
});
});
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { PaymentMethodType } from "../../../billing/enums";
import { OrganizationNoPaymentMethodCreateRequest } from "../../../billing/models/request/organization-no-payment-method-create-request";

export class OrganizationCreateRequest extends OrganizationNoPaymentMethodCreateRequest {
paymentMethodType: PaymentMethodType;
paymentToken: string;
paymentMethodType!: PaymentMethodType;
paymentToken: string = "";
skipTrial?: boolean;
coupons?: string[];
}
Loading
Loading