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

Merge branch 'master' into feature/org-admin-refresh

This commit is contained in:
Vincent Salucci
2022-08-29 10:48:20 -05:00
279 changed files with 4467 additions and 2169 deletions

View File

@@ -1,4 +1,3 @@
import { OrganizationApiKeyType } from "../enums/organizationApiKeyType";
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest";
import { AttachmentRequest } from "../models/request/attachmentRequest";
@@ -28,23 +27,14 @@ import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest
import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest";
import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest";
import { ImportCiphersRequest } from "../models/request/importCiphersRequest";
import { ImportDirectoryRequest } from "../models/request/importDirectoryRequest";
import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest";
import { KdfRequest } from "../models/request/kdfRequest";
import { KeyConnectorUserKeyRequest } from "../models/request/keyConnectorUserKeyRequest";
import { KeysRequest } from "../models/request/keysRequest";
import { OrganizationSponsorshipCreateRequest } from "../models/request/organization/organizationSponsorshipCreateRequest";
import { OrganizationSponsorshipRedeemRequest } from "../models/request/organization/organizationSponsorshipRedeemRequest";
import { OrganizationSsoRequest } from "../models/request/organization/organizationSsoRequest";
import { OrganizationApiKeyRequest } from "../models/request/organizationApiKeyRequest";
import { OrganizationConnectionRequest } from "../models/request/organizationConnectionRequest";
import { OrganizationCreateRequest } from "../models/request/organizationCreateRequest";
import { OrganizationImportRequest } from "../models/request/organizationImportRequest";
import { OrganizationKeysRequest } from "../models/request/organizationKeysRequest";
import { OrganizationSubscriptionUpdateRequest } from "../models/request/organizationSubscriptionUpdateRequest";
import { OrganizationTaxInfoUpdateRequest } from "../models/request/organizationTaxInfoUpdateRequest";
import { OrganizationUpdateRequest } from "../models/request/organizationUpdateRequest";
import { OrganizationUpgradeRequest } from "../models/request/organizationUpgradeRequest";
import { OrganizationUserAcceptRequest } from "../models/request/organizationUserAcceptRequest";
import { OrganizationUserBulkConfirmRequest } from "../models/request/organizationUserBulkConfirmRequest";
import { OrganizationUserBulkRequest } from "../models/request/organizationUserBulkRequest";
@@ -69,7 +59,6 @@ import { ProviderUserConfirmRequest } from "../models/request/provider/providerU
import { ProviderUserInviteRequest } from "../models/request/provider/providerUserInviteRequest";
import { ProviderUserUpdateRequest } from "../models/request/provider/providerUserUpdateRequest";
import { RegisterRequest } from "../models/request/registerRequest";
import { SeatRequest } from "../models/request/seatRequest";
import { SecretVerificationRequest } from "../models/request/secretVerificationRequest";
import { SelectionReadOnlyRequest } from "../models/request/selectionReadOnlyRequest";
import { SendAccessRequest } from "../models/request/sendAccessRequest";
@@ -90,7 +79,6 @@ import { UpdateTwoFactorEmailRequest } from "../models/request/updateTwoFactorEm
import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/updateTwoFactorWebAuthnDeleteRequest";
import { UpdateTwoFactorWebAuthnRequest } from "../models/request/updateTwoFactorWebAuthnRequest";
import { UpdateTwoFactorYubioOtpRequest } from "../models/request/updateTwoFactorYubioOtpRequest";
import { VerifyBankRequest } from "../models/request/verifyBankRequest";
import { VerifyDeleteRecoverRequest } from "../models/request/verifyDeleteRecoverRequest";
import { VerifyEmailRequest } from "../models/request/verifyEmailRequest";
import { ApiKeyResponse } from "../models/response/apiKeyResponse";
@@ -98,7 +86,6 @@ import { AttachmentResponse } from "../models/response/attachmentResponse";
import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse";
import { BillingHistoryResponse } from "../models/response/billingHistoryResponse";
import { BillingPaymentResponse } from "../models/response/billingPaymentResponse";
import { BillingResponse } from "../models/response/billingResponse";
import { BreachAccountResponse } from "../models/response/breachAccountResponse";
import { CipherResponse } from "../models/response/cipherResponse";
import {
@@ -120,18 +107,12 @@ import { IdentityTokenResponse } from "../models/response/identityTokenResponse"
import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse";
import { KeyConnectorUserKeyResponse } from "../models/response/keyConnectorUserKeyResponse";
import { ListResponse } from "../models/response/listResponse";
import { OrganizationSsoResponse } from "../models/response/organization/organizationSsoResponse";
import { OrganizationApiKeyInformationResponse } from "../models/response/organizationApiKeyInformationResponse";
import { OrganizationAutoEnrollStatusResponse } from "../models/response/organizationAutoEnrollStatusResponse";
import {
OrganizationConnectionConfigApis,
OrganizationConnectionResponse,
} from "../models/response/organizationConnectionResponse";
import { OrganizationExportResponse } from "../models/response/organizationExportResponse";
import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse";
import { OrganizationResponse } from "../models/response/organizationResponse";
import { OrganizationSponsorshipSyncStatusResponse } from "../models/response/organizationSponsorshipSyncStatusResponse";
import { OrganizationSubscriptionResponse } from "../models/response/organizationSubscriptionResponse";
import { OrganizationUserBulkPublicKeyResponse } from "../models/response/organizationUserBulkPublicKeyResponse";
import { OrganizationUserBulkResponse } from "../models/response/organizationUserBulkResponse";
import {
@@ -214,7 +195,7 @@ export abstract class ApiService {
postReinstatePremium: () => Promise<any>;
postCancelPremium: () => Promise<any>;
postAccountStorage: (request: StorageRequest) => Promise<PaymentResponse>;
postAccountPayment: (request: PaymentRequest) => Promise<any>;
postAccountPayment: (request: PaymentRequest) => Promise<void>;
postAccountLicense: (data: FormData) => Promise<any>;
postAccountKey: (request: UpdateKeyRequest) => Promise<any>;
postAccountKeys: (request: KeysRequest) => Promise<any>;
@@ -415,7 +396,7 @@ export abstract class ApiService {
organizationId: string,
userId: string,
request: OrganizationUserResetPasswordEnrollmentRequest
) => Promise<any>;
) => Promise<void>;
putOrganizationUserResetPassword: (
organizationId: string,
id: string,
@@ -438,7 +419,6 @@ export abstract class ApiService {
) => Promise<ListResponse<OrganizationUserBulkResponse>>;
getSync: () => Promise<SyncResponse>;
postImportDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<any>;
postPublicImportDirectory: (request: OrganizationImportRequest) => Promise<any>;
getSettingsDomains: () => Promise<DomainsResponse>;
@@ -511,10 +491,6 @@ export abstract class ApiService {
request: EmergencyAccessPasswordRequest
) => Promise<any>;
postEmergencyAccessView: (id: string) => Promise<EmergencyAccessViewResponse>;
getOrganization: (id: string) => Promise<OrganizationResponse>;
getOrganizationBilling: (id: string) => Promise<BillingResponse>;
getOrganizationSubscription: (id: string) => Promise<OrganizationSubscriptionResponse>;
getCloudCommunicationsEnabled: () => Promise<boolean>;
abstract getOrganizationConnection<TConfig extends OrganizationConnectionConfigApis>(
id: string,
@@ -531,59 +507,8 @@ export abstract class ApiService {
organizationConnectionId: string
): Promise<OrganizationConnectionResponse<TConfig>>;
deleteOrganizationConnection: (id: string) => Promise<void>;
getOrganizationLicense: (id: string, installationId: string) => Promise<any>;
getOrganizationTaxInfo: (id: string) => Promise<TaxInfoResponse>;
getOrganizationAutoEnrollStatus: (
identifier: string
) => Promise<OrganizationAutoEnrollStatusResponse>;
getOrganizationSso: (id: string) => Promise<OrganizationSsoResponse>;
postOrganization: (request: OrganizationCreateRequest) => Promise<OrganizationResponse>;
putOrganization: (
id: string,
request: OrganizationUpdateRequest
) => Promise<OrganizationResponse>;
putOrganizationTaxInfo: (id: string, request: OrganizationTaxInfoUpdateRequest) => Promise<any>;
postLeaveOrganization: (id: string) => Promise<any>;
postOrganizationLicense: (data: FormData) => Promise<OrganizationResponse>;
postOrganizationLicenseUpdate: (id: string, data: FormData) => Promise<any>;
postOrganizationApiKey: (
id: string,
request: OrganizationApiKeyRequest
) => Promise<ApiKeyResponse>;
getOrganizationApiKeyInformation: (
id: string,
type?: OrganizationApiKeyType
) => Promise<ListResponse<OrganizationApiKeyInformationResponse>>;
postOrganizationRotateApiKey: (
id: string,
request: OrganizationApiKeyRequest
) => Promise<ApiKeyResponse>;
postOrganizationSso: (
id: string,
request: OrganizationSsoRequest
) => Promise<OrganizationSsoResponse>;
postOrganizationUpgrade: (
id: string,
request: OrganizationUpgradeRequest
) => Promise<PaymentResponse>;
postOrganizationUpdateSubscription: (
id: string,
request: OrganizationSubscriptionUpdateRequest
) => Promise<void>;
postOrganizationSeat: (id: string, request: SeatRequest) => Promise<PaymentResponse>;
postOrganizationStorage: (id: string, request: StorageRequest) => Promise<any>;
postOrganizationPayment: (id: string, request: PaymentRequest) => Promise<any>;
postOrganizationVerifyBank: (id: string, request: VerifyBankRequest) => Promise<any>;
postOrganizationCancel: (id: string) => Promise<any>;
postOrganizationReinstate: (id: string) => Promise<any>;
deleteOrganization: (id: string, request: SecretVerificationRequest) => Promise<any>;
getPlans: () => Promise<ListResponse<PlanResponse>>;
getTaxRates: () => Promise<ListResponse<TaxRateResponse>>;
getOrganizationKeys: (id: string) => Promise<OrganizationKeysResponse>;
postOrganizationKeys: (
id: string,
request: OrganizationKeysRequest
) => Promise<OrganizationKeysResponse>;
postProviderSetup: (id: string, request: ProviderSetupRequest) => Promise<ProviderResponse>;
getProvider: (id: string) => Promise<ProviderResponse>;
@@ -673,7 +598,7 @@ export abstract class ApiService {
) => Promise<ListResponse<EventResponse>>;
postEventsCollect: (request: EventRequest[]) => Promise<any>;
deleteSsoUser: (organizationId: string) => Promise<any>;
deleteSsoUser: (organizationId: string) => Promise<void>;
getSsoUserIdentifier: () => Promise<string>;
getUserPublicKey: (id: string) => Promise<UserKeyResponse>;

View File

@@ -1,5 +1,9 @@
export interface MessageBase {
command: string;
}
export abstract class BroadcasterService {
send: (message: any, id?: string) => void;
subscribe: (id: string, messageCallback: (message: any) => any) => void;
send: (message: MessageBase, id?: string) => void;
subscribe: (id: string, messageCallback: (message: MessageBase) => void) => void;
unsubscribe: (id: string) => void;
}

View File

@@ -0,0 +1,61 @@
import { OrganizationApiKeyType } from "../../enums/organizationApiKeyType";
import { ImportDirectoryRequest } from "../../models/request/importDirectoryRequest";
import { OrganizationSsoRequest } from "../../models/request/organization/organizationSsoRequest";
import { OrganizationApiKeyRequest } from "../../models/request/organizationApiKeyRequest";
import { OrganizationCreateRequest } from "../../models/request/organizationCreateRequest";
import { OrganizationKeysRequest } from "../../models/request/organizationKeysRequest";
import { OrganizationSubscriptionUpdateRequest } from "../../models/request/organizationSubscriptionUpdateRequest";
import { OrganizationTaxInfoUpdateRequest } from "../../models/request/organizationTaxInfoUpdateRequest";
import { OrganizationUpdateRequest } from "../../models/request/organizationUpdateRequest";
import { OrganizationUpgradeRequest } from "../../models/request/organizationUpgradeRequest";
import { PaymentRequest } from "../../models/request/paymentRequest";
import { SeatRequest } from "../../models/request/seatRequest";
import { SecretVerificationRequest } from "../../models/request/secretVerificationRequest";
import { StorageRequest } from "../../models/request/storageRequest";
import { VerifyBankRequest } from "../../models/request/verifyBankRequest";
import { ApiKeyResponse } from "../../models/response/apiKeyResponse";
import { BillingResponse } from "../../models/response/billingResponse";
import { ListResponse } from "../../models/response/listResponse";
import { OrganizationSsoResponse } from "../../models/response/organization/organizationSsoResponse";
import { OrganizationApiKeyInformationResponse } from "../../models/response/organizationApiKeyInformationResponse";
import { OrganizationAutoEnrollStatusResponse } from "../../models/response/organizationAutoEnrollStatusResponse";
import { OrganizationKeysResponse } from "../../models/response/organizationKeysResponse";
import { OrganizationResponse } from "../../models/response/organizationResponse";
import { OrganizationSubscriptionResponse } from "../../models/response/organizationSubscriptionResponse";
import { PaymentResponse } from "../../models/response/paymentResponse";
import { TaxInfoResponse } from "../../models/response/taxInfoResponse";
export class OrganizationApiServiceAbstraction {
get: (id: string) => Promise<OrganizationResponse>;
getBilling: (id: string) => Promise<BillingResponse>;
getSubscription: (id: string) => Promise<OrganizationSubscriptionResponse>;
getLicense: (id: string, installationId: string) => Promise<unknown>;
getAutoEnrollStatus: (identifier: string) => Promise<OrganizationAutoEnrollStatusResponse>;
create: (request: OrganizationCreateRequest) => 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>;
updateSubscription: (id: string, request: OrganizationSubscriptionUpdateRequest) => Promise<void>;
updateSeats: (id: string, request: SeatRequest) => Promise<PaymentResponse>;
updateStorage: (id: string, request: StorageRequest) => Promise<PaymentResponse>;
verifyBank: (id: string, request: VerifyBankRequest) => Promise<void>;
cancel: (id: string) => Promise<void>;
reinstate: (id: string) => Promise<void>;
leave: (id: string) => Promise<void>;
delete: (id: string, request: SecretVerificationRequest) => Promise<void>;
updateLicense: (id: string, data: FormData) => Promise<void>;
importDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<void>;
getOrCreateApiKey: (id: string, request: OrganizationApiKeyRequest) => Promise<ApiKeyResponse>;
getApiKeyInformation: (
id: string,
organizationApiKeyType?: OrganizationApiKeyType
) => Promise<ListResponse<OrganizationApiKeyInformationResponse>>;
rotateApiKey: (id: string, request: OrganizationApiKeyRequest) => Promise<ApiKeyResponse>;
getTaxInfo: (id: string) => Promise<TaxInfoResponse>;
updateTaxInfo: (id: string, request: OrganizationTaxInfoUpdateRequest) => 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>;
}

View File

@@ -1,6 +1,6 @@
export abstract class VaultTimeoutService {
checkVaultTimeout: () => Promise<void>;
lock: (allowSoftLock?: boolean, userId?: string) => Promise<void>;
lock: (userId?: string) => Promise<void>;
logOut: (userId?: string) => Promise<void>;
setVaultTimeoutOptions: (vaultTimeout: number, vaultTimeoutAction: string) => Promise<void>;
getVaultTimeout: () => Promise<number>;

View File

@@ -0,0 +1,41 @@
import { ApiService } from "../abstractions/api.service";
import { Forwarder } from "./forwarder";
import { ForwarderOptions } from "./forwarderOptions";
export class AnonAddyForwarder implements Forwarder {
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
if (options.apiKey == null || options.apiKey === "") {
throw "Invalid AnonAddy API token.";
}
if (options.anonaddy?.domain == null || options.anonaddy.domain === "") {
throw "Invalid AnonAddy domain.";
}
const requestInit: RequestInit = {
redirect: "manual",
cache: "no-store",
method: "POST",
headers: new Headers({
Authorization: "Bearer " + options.apiKey,
"Content-Type": "application/json",
}),
};
const url = "https://app.anonaddy.com/api/v1/aliases";
requestInit.body = JSON.stringify({
domain: options.anonaddy.domain,
description:
(options.website != null ? "Website: " + options.website + ". " : "") +
"Generated by Bitwarden.",
});
const request = new Request(url, requestInit);
const response = await apiService.nativeFetch(request);
if (response.status === 200 || response.status === 201) {
const json = await response.json();
return json?.data?.email;
}
if (response.status === 401) {
throw "Invalid AnonAddy API token.";
}
throw "Unknown AnonAddy error occurred.";
}
}

View File

@@ -0,0 +1,33 @@
import { ApiService } from "../abstractions/api.service";
import { Forwarder } from "./forwarder";
import { ForwarderOptions } from "./forwarderOptions";
export class DuckDuckGoForwarder implements Forwarder {
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
if (options.apiKey == null || options.apiKey === "") {
throw "Invalid DuckDuckGo API token.";
}
const requestInit: RequestInit = {
redirect: "manual",
cache: "no-store",
method: "POST",
headers: new Headers({
Authorization: "Bearer " + options.apiKey,
"Content-Type": "application/json",
}),
};
const url = "https://quack.duckduckgo.com/api/email/addresses";
const request = new Request(url, requestInit);
const response = await apiService.nativeFetch(request);
if (response.status === 200 || response.status === 201) {
const json = await response.json();
if (json.address) {
return `${json.address}@duck.com`;
}
} else if (response.status === 401) {
throw "Invalid DuckDuckGo API token.";
}
throw "Unknown DuckDuckGo error occurred.";
}
}

View File

@@ -0,0 +1,96 @@
import { ApiService } from "../abstractions/api.service";
import { Forwarder } from "./forwarder";
import { ForwarderOptions } from "./forwarderOptions";
export class FastmailForwarder implements Forwarder {
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
if (options.apiKey == null || options.apiKey === "") {
throw "Invalid Fastmail API token.";
}
const accountId = await this.getAccountId(apiService, options);
if (accountId == null || accountId === "") {
throw "Unable to obtain Fastmail masked email account ID.";
}
const requestInit: RequestInit = {
redirect: "manual",
cache: "no-store",
method: "POST",
headers: new Headers({
Authorization: "Bearer " + options.apiKey,
"Content-Type": "application/json",
}),
};
const url = "https://api.fastmail.com/jmap/api/";
requestInit.body = JSON.stringify({
using: ["https://www.fastmail.com/dev/maskedemail", "urn:ietf:params:jmap:core"],
methodCalls: [
[
"MaskedEmail/set",
{
accountId: accountId,
create: {
"new-masked-email": {
state: "enabled",
description: "",
url: options.website,
emailPrefix: options.fastmail.prefix,
},
},
},
"0",
],
],
});
const request = new Request(url, requestInit);
const response = await apiService.nativeFetch(request);
if (response.status === 200) {
const json = await response.json();
if (
json.methodResponses != null &&
json.methodResponses.length > 0 &&
json.methodResponses[0].length > 0
) {
if (json.methodResponses[0][0] === "MaskedEmail/set") {
if (json.methodResponses[0][1]?.created?.["new-masked-email"] != null) {
return json.methodResponses[0][1]?.created?.["new-masked-email"]?.email;
}
if (json.methodResponses[0][1]?.notCreated?.["new-masked-email"] != null) {
throw (
"Fastmail error: " +
json.methodResponses[0][1]?.notCreated?.["new-masked-email"]?.description
);
}
} else if (json.methodResponses[0][0] === "error") {
throw "Fastmail error: " + json.methodResponses[0][1]?.description;
}
}
}
if (response.status === 401 || response.status === 403) {
throw "Invalid Fastmail API token.";
}
throw "Unknown Fastmail error occurred.";
}
private async getAccountId(apiService: ApiService, options: ForwarderOptions): Promise<string> {
const requestInit: RequestInit = {
cache: "no-store",
method: "GET",
headers: new Headers({
Authorization: "Bearer " + options.apiKey,
}),
};
const url = "https://api.fastmail.com/.well-known/jmap";
const request = new Request(url, requestInit);
const response = await apiService.nativeFetch(request);
if (response.status === 200) {
const json = await response.json();
if (json.primaryAccounts != null) {
return json.primaryAccounts["https://www.fastmail.com/dev/maskedemail"];
}
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
import { ApiService } from "../abstractions/api.service";
import { Forwarder } from "./forwarder";
import { ForwarderOptions } from "./forwarderOptions";
export class FirefoxRelayForwarder implements Forwarder {
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
if (options.apiKey == null || options.apiKey === "") {
throw "Invalid Firefox Relay API token.";
}
const requestInit: RequestInit = {
redirect: "manual",
cache: "no-store",
method: "POST",
headers: new Headers({
Authorization: "Token " + options.apiKey,
"Content-Type": "application/json",
}),
};
const url = "https://relay.firefox.com/api/v1/relayaddresses/";
requestInit.body = JSON.stringify({
enabled: true,
generated_for: options.website,
description:
(options.website != null ? options.website + " - " : "") + "Generated by Bitwarden.",
});
const request = new Request(url, requestInit);
const response = await apiService.nativeFetch(request);
if (response.status === 200 || response.status === 201) {
const json = await response.json();
return json?.full_address;
}
if (response.status === 401) {
throw "Invalid Firefox Relay API token.";
}
throw "Unknown Firefox Relay error occurred.";
}
}

View File

@@ -0,0 +1,7 @@
import { ApiService } from "../abstractions/api.service";
import { ForwarderOptions } from "./forwarderOptions";
export interface Forwarder {
generate(apiService: ApiService, options: ForwarderOptions): Promise<string>;
}

View File

@@ -0,0 +1,14 @@
export class ForwarderOptions {
apiKey: string;
website: string;
fastmail = new FastmailForwarderOptions();
anonaddy = new AnonAddyForwarderOptions();
}
export class FastmailForwarderOptions {
prefix: string;
}
export class AnonAddyForwarderOptions {
domain: string;
}

View File

@@ -0,0 +1,48 @@
import { ApiService } from "../abstractions/api.service";
import { Forwarder } from "./forwarder";
import { ForwarderOptions } from "./forwarderOptions";
export class SimpleLoginForwarder implements Forwarder {
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
if (options.apiKey == null || options.apiKey === "") {
throw "Invalid SimpleLogin API key.";
}
const requestInit: RequestInit = {
redirect: "manual",
cache: "no-store",
method: "POST",
headers: new Headers({
Authentication: options.apiKey,
"Content-Type": "application/json",
}),
};
let url = "https://app.simplelogin.io/api/alias/random/new";
if (options.website != null) {
url += "?hostname=" + options.website;
}
requestInit.body = JSON.stringify({
note:
(options.website != null ? "Website: " + options.website + ". " : "") +
"Generated by Bitwarden.",
});
const request = new Request(url, requestInit);
const response = await apiService.nativeFetch(request);
if (response.status === 200 || response.status === 201) {
const json = await response.json();
return json.alias;
}
if (response.status === 401) {
throw "Invalid SimpleLogin API key.";
}
try {
const json = await response.json();
if (json?.error != null) {
throw "SimpleLogin error:" + json.error;
}
} catch {
// Do nothing...
}
throw "Unknown SimpleLogin error occurred.";
}
}

View File

@@ -0,0 +1,4 @@
export enum EncryptedExportType {
AccountEncrypted = 0,
FileEncrypted = 1,
}

View File

@@ -35,7 +35,7 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im
if (!(await this.checkPassword(parsedData))) {
result.success = false;
result.errorMessage = this.i18nService.t("importEncKeyError");
result.errorMessage = this.i18nService.t("invalidFilePassword");
return result;
}

View File

@@ -0,0 +1,99 @@
import { flagEnabled, devFlagEnabled, devFlagValue } from "./flags";
describe("flagEnabled", () => {
beforeEach(() => {
process.env.FLAGS = JSON.stringify({});
});
it("returns true by default", () => {
expect(flagEnabled<any>("nonExistentFlag")).toBe(true);
});
it("returns true if enabled", () => {
process.env.FLAGS = JSON.stringify({
newFeature: true,
});
expect(flagEnabled<any>("newFeature")).toBe(true);
});
it("returns false if disabled", () => {
process.env.FLAGS = JSON.stringify({
newFeature: false,
});
expect(flagEnabled<any>("newFeature")).toBe(false);
});
});
describe("devFlagEnabled", () => {
beforeEach(() => {
process.env.DEV_FLAGS = JSON.stringify({});
});
describe("in a development environment", () => {
beforeEach(() => {
process.env.ENV = "development";
});
it("returns true by default", () => {
expect(devFlagEnabled<any>("nonExistentFlag")).toBe(true);
});
it("returns true if enabled", () => {
process.env.DEV_FLAGS = JSON.stringify({
devHack: true,
});
expect(devFlagEnabled<any>("devHack")).toBe(true);
});
it("returns true if truthy", () => {
process.env.DEV_FLAGS = JSON.stringify({
devHack: { key: 3 },
});
expect(devFlagEnabled<any>("devHack")).toBe(true);
});
it("returns false if disabled", () => {
process.env.DEV_FLAGS = JSON.stringify({
devHack: false,
});
expect(devFlagEnabled<any>("devHack")).toBe(false);
});
});
it("always returns false in prod", () => {
process.env.ENV = "production";
process.env.DEV_FLAGS = JSON.stringify({
devHack: true,
});
expect(devFlagEnabled<any>("devHack")).toBe(false);
});
});
describe("devFlagValue", () => {
beforeEach(() => {
process.env.DEV_FLAGS = JSON.stringify({});
process.env.ENV = "development";
});
it("throws if dev flag is disabled", () => {
process.env.DEV_FLAGS = JSON.stringify({
devHack: false,
});
expect(() => devFlagValue<any>("devHack")).toThrow("it is protected by a disabled dev flag");
});
it("returns the dev flag value", () => {
process.env.DEV_FLAGS = JSON.stringify({
devHack: "Hello world",
});
expect(devFlagValue<any>("devHack")).toBe("Hello world");
});
});

View File

@@ -0,0 +1,61 @@
// required to avoid linting errors when there are no flags
/* eslint-disable @typescript-eslint/ban-types */
export type SharedFlags = {};
// required to avoid linting errors when there are no flags
/* eslint-disable @typescript-eslint/ban-types */
export type SharedDevFlags = {};
function getFlags<T>(envFlags: string | T): T {
if (typeof envFlags === "string") {
return JSON.parse(envFlags) as T;
} else {
return envFlags as T;
}
}
/**
* Gets the value of a feature flag from environment.
* All flags default to "on" (true).
* Only use for shared code in `libs`, otherwise use the client-specific function.
* @param flag The name of the feature flag to check
* @returns The value of the flag
*/
export function flagEnabled<Flags extends SharedFlags>(flag: keyof Flags): boolean {
const flags = getFlags<Flags>(process.env.FLAGS);
return flags[flag] == null || !!flags[flag];
}
/**
* Gets the value of a dev flag from environment.
* Will always return false unless in development.
* Only use for shared code in `libs`, otherwise use the client-specific function.
* @param flag The name of the dev flag to check
* @returns The value of the flag
*/
export function devFlagEnabled<DevFlags extends SharedDevFlags>(flag: keyof DevFlags): boolean {
if (process.env.ENV !== "development") {
return false;
}
const devFlags = getFlags<DevFlags>(process.env.DEV_FLAGS);
return devFlags[flag] == null || !!devFlags[flag];
}
/**
* Gets the value of a dev flag from environment.
* Will always return false unless in development.
* @param flag The name of the dev flag to check
* @returns The value of the flag
* @throws Error if the flag is not enabled
*/
export function devFlagValue<DevFlags extends SharedDevFlags>(
flag: keyof DevFlags
): DevFlags[keyof DevFlags] {
if (!devFlagEnabled(flag)) {
throw new Error(`This method should not be called, it is protected by a disabled dev flag.`);
}
const devFlags = getFlags<DevFlags>(process.env.DEV_FLAGS);
return devFlags[flag];
}

View File

@@ -72,7 +72,7 @@ export class Send extends Domain {
const model = new SendView(this);
let cryptoService: CryptoService;
const containerService = (Utils.global as any).bitwardenContainerService;
const containerService = Utils.global.bitwardenContainerService;
if (containerService) {
cryptoService = containerService.getCryptoService();
} else {

View File

@@ -4,7 +4,6 @@ import { EnvironmentService } from "../abstractions/environment.service";
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
import { TokenService } from "../abstractions/token.service";
import { DeviceType } from "../enums/deviceType";
import { OrganizationApiKeyType } from "../enums/organizationApiKeyType";
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
import { Utils } from "../misc/utils";
import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest";
@@ -36,23 +35,14 @@ import { PasswordTokenRequest } from "../models/request/identityToken/passwordTo
import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest";
import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequestTwoFactor";
import { ImportCiphersRequest } from "../models/request/importCiphersRequest";
import { ImportDirectoryRequest } from "../models/request/importDirectoryRequest";
import { ImportOrganizationCiphersRequest } from "../models/request/importOrganizationCiphersRequest";
import { KdfRequest } from "../models/request/kdfRequest";
import { KeyConnectorUserKeyRequest } from "../models/request/keyConnectorUserKeyRequest";
import { KeysRequest } from "../models/request/keysRequest";
import { OrganizationSponsorshipCreateRequest } from "../models/request/organization/organizationSponsorshipCreateRequest";
import { OrganizationSponsorshipRedeemRequest } from "../models/request/organization/organizationSponsorshipRedeemRequest";
import { OrganizationSsoRequest } from "../models/request/organization/organizationSsoRequest";
import { OrganizationApiKeyRequest } from "../models/request/organizationApiKeyRequest";
import { OrganizationConnectionRequest } from "../models/request/organizationConnectionRequest";
import { OrganizationCreateRequest } from "../models/request/organizationCreateRequest";
import { OrganizationImportRequest } from "../models/request/organizationImportRequest";
import { OrganizationKeysRequest } from "../models/request/organizationKeysRequest";
import { OrganizationSubscriptionUpdateRequest } from "../models/request/organizationSubscriptionUpdateRequest";
import { OrganizationTaxInfoUpdateRequest } from "../models/request/organizationTaxInfoUpdateRequest";
import { OrganizationUpdateRequest } from "../models/request/organizationUpdateRequest";
import { OrganizationUpgradeRequest } from "../models/request/organizationUpgradeRequest";
import { OrganizationUserAcceptRequest } from "../models/request/organizationUserAcceptRequest";
import { OrganizationUserBulkConfirmRequest } from "../models/request/organizationUserBulkConfirmRequest";
import { OrganizationUserBulkRequest } from "../models/request/organizationUserBulkRequest";
@@ -77,7 +67,6 @@ import { ProviderUserConfirmRequest } from "../models/request/provider/providerU
import { ProviderUserInviteRequest } from "../models/request/provider/providerUserInviteRequest";
import { ProviderUserUpdateRequest } from "../models/request/provider/providerUserUpdateRequest";
import { RegisterRequest } from "../models/request/registerRequest";
import { SeatRequest } from "../models/request/seatRequest";
import { SecretVerificationRequest } from "../models/request/secretVerificationRequest";
import { SelectionReadOnlyRequest } from "../models/request/selectionReadOnlyRequest";
import { SendAccessRequest } from "../models/request/sendAccessRequest";
@@ -98,7 +87,6 @@ import { UpdateTwoFactorEmailRequest } from "../models/request/updateTwoFactorEm
import { UpdateTwoFactorWebAuthnDeleteRequest } from "../models/request/updateTwoFactorWebAuthnDeleteRequest";
import { UpdateTwoFactorWebAuthnRequest } from "../models/request/updateTwoFactorWebAuthnRequest";
import { UpdateTwoFactorYubioOtpRequest } from "../models/request/updateTwoFactorYubioOtpRequest";
import { VerifyBankRequest } from "../models/request/verifyBankRequest";
import { VerifyDeleteRecoverRequest } from "../models/request/verifyDeleteRecoverRequest";
import { VerifyEmailRequest } from "../models/request/verifyEmailRequest";
import { ApiKeyResponse } from "../models/response/apiKeyResponse";
@@ -106,7 +94,6 @@ import { AttachmentResponse } from "../models/response/attachmentResponse";
import { AttachmentUploadDataResponse } from "../models/response/attachmentUploadDataResponse";
import { BillingHistoryResponse } from "../models/response/billingHistoryResponse";
import { BillingPaymentResponse } from "../models/response/billingPaymentResponse";
import { BillingResponse } from "../models/response/billingResponse";
import { BreachAccountResponse } from "../models/response/breachAccountResponse";
import { CipherResponse } from "../models/response/cipherResponse";
import {
@@ -129,18 +116,12 @@ import { IdentityTokenResponse } from "../models/response/identityTokenResponse"
import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse";
import { KeyConnectorUserKeyResponse } from "../models/response/keyConnectorUserKeyResponse";
import { ListResponse } from "../models/response/listResponse";
import { OrganizationSsoResponse } from "../models/response/organization/organizationSsoResponse";
import { OrganizationApiKeyInformationResponse } from "../models/response/organizationApiKeyInformationResponse";
import { OrganizationAutoEnrollStatusResponse } from "../models/response/organizationAutoEnrollStatusResponse";
import {
OrganizationConnectionConfigApis,
OrganizationConnectionResponse,
} from "../models/response/organizationConnectionResponse";
import { OrganizationExportResponse } from "../models/response/organizationExportResponse";
import { OrganizationKeysResponse } from "../models/response/organizationKeysResponse";
import { OrganizationResponse } from "../models/response/organizationResponse";
import { OrganizationSponsorshipSyncStatusResponse } from "../models/response/organizationSponsorshipSyncStatusResponse";
import { OrganizationSubscriptionResponse } from "../models/response/organizationSubscriptionResponse";
import { OrganizationUserBulkPublicKeyResponse } from "../models/response/organizationUserBulkPublicKeyResponse";
import { OrganizationUserBulkResponse } from "../models/response/organizationUserBulkResponse";
import {
@@ -391,7 +372,7 @@ export class ApiService implements ApiServiceAbstraction {
return new PaymentResponse(r);
}
postAccountPayment(request: PaymentRequest): Promise<any> {
postAccountPayment(request: PaymentRequest): Promise<void> {
return this.send("POST", "/accounts/payment", request, true, false);
}
@@ -431,7 +412,7 @@ export class ApiService implements ApiServiceAbstraction {
return this.send("POST", "/accounts/kdf", request, true, false);
}
async deleteSsoUser(organizationId: string): Promise<any> {
async deleteSsoUser(organizationId: string): Promise<void> {
return this.send("DELETE", "/accounts/sso/" + organizationId, null, true, false);
}
@@ -1067,19 +1048,6 @@ export class ApiService implements ApiServiceAbstraction {
return new OrganizationUserResetPasswordDetailsReponse(r);
}
async getOrganizationAutoEnrollStatus(
identifier: string
): Promise<OrganizationAutoEnrollStatusResponse> {
const r = await this.send(
"GET",
"/organizations/" + identifier + "/auto-enroll-status",
null,
true,
true
);
return new OrganizationAutoEnrollStatusResponse(r);
}
postOrganizationUserInvite(
organizationId: string,
request: OrganizationUserInviteRequest
@@ -1205,7 +1173,7 @@ export class ApiService implements ApiServiceAbstraction {
organizationId: string,
userId: string,
request: OrganizationUserResetPasswordEnrollmentRequest
): Promise<any> {
): Promise<void> {
return this.send(
"PUT",
"/organizations/" + organizationId + "/users/" + userId + "/reset-password-enrollment",
@@ -1308,10 +1276,6 @@ export class ApiService implements ApiServiceAbstraction {
return new ListResponse(r, PlanResponse);
}
async postImportDirectory(organizationId: string, request: ImportDirectoryRequest): Promise<any> {
return this.send("POST", "/organizations/" + organizationId + "/import", request, true, false);
}
async postPublicImportDirectory(request: OrganizationImportRequest): Promise<any> {
return this.send("POST", "/public/organization/import", request, true, false);
}
@@ -1614,21 +1578,6 @@ export class ApiService implements ApiServiceAbstraction {
// Organization APIs
async getOrganization(id: string): Promise<OrganizationResponse> {
const r = await this.send("GET", "/organizations/" + id, null, true, true);
return new OrganizationResponse(r);
}
async getOrganizationBilling(id: string): Promise<BillingResponse> {
const r = await this.send("GET", "/organizations/" + id + "/billing", null, true, true);
return new BillingResponse(r);
}
async getOrganizationSubscription(id: string): Promise<OrganizationSubscriptionResponse> {
const r = await this.send("GET", "/organizations/" + id + "/subscription", null, true, true);
return new OrganizationSubscriptionResponse(r);
}
async getCloudCommunicationsEnabled(): Promise<boolean> {
const r = await this.send("GET", "/organizations/connections/enabled", null, true, true);
return r as boolean;
@@ -1670,159 +1619,6 @@ export class ApiService implements ApiServiceAbstraction {
return this.send("DELETE", "/organizations/connections/" + id, null, true, false);
}
async getOrganizationLicense(id: string, installationId: string): Promise<any> {
return this.send(
"GET",
"/organizations/" + id + "/license?installationId=" + installationId,
null,
true,
true
);
}
async getOrganizationTaxInfo(id: string): Promise<TaxInfoResponse> {
const r = await this.send("GET", "/organizations/" + id + "/tax", null, true, true);
return new TaxInfoResponse(r);
}
async getOrganizationSso(id: string): Promise<OrganizationSsoResponse> {
const r = await this.send("GET", "/organizations/" + id + "/sso", null, true, true);
return new OrganizationSsoResponse(r);
}
async postOrganization(request: OrganizationCreateRequest): Promise<OrganizationResponse> {
const r = await this.send("POST", "/organizations", request, true, true);
return new OrganizationResponse(r);
}
async putOrganization(
id: string,
request: OrganizationUpdateRequest
): Promise<OrganizationResponse> {
const r = await this.send("PUT", "/organizations/" + id, request, true, true);
return new OrganizationResponse(r);
}
async putOrganizationTaxInfo(
id: string,
request: OrganizationTaxInfoUpdateRequest
): Promise<any> {
return this.send("PUT", "/organizations/" + id + "/tax", request, true, false);
}
postLeaveOrganization(id: string): Promise<any> {
return this.send("POST", "/organizations/" + id + "/leave", null, true, false);
}
async postOrganizationLicense(data: FormData): Promise<OrganizationResponse> {
const r = await this.send("POST", "/organizations/license", data, true, true);
return new OrganizationResponse(r);
}
async postOrganizationLicenseUpdate(id: string, data: FormData): Promise<any> {
return this.send("POST", "/organizations/" + id + "/license", data, true, false);
}
async postOrganizationApiKey(
id: string,
request: OrganizationApiKeyRequest
): Promise<ApiKeyResponse> {
const r = await this.send("POST", "/organizations/" + id + "/api-key", request, true, true);
return new ApiKeyResponse(r);
}
async getOrganizationApiKeyInformation(
id: string,
type: OrganizationApiKeyType = null
): Promise<ListResponse<OrganizationApiKeyInformationResponse>> {
const uri =
type === null
? "/organizations/" + id + "/api-key-information"
: "/organizations/" + id + "/api-key-information/" + type;
const r = await this.send("GET", uri, null, true, true);
return new ListResponse(r, OrganizationApiKeyInformationResponse);
}
async postOrganizationRotateApiKey(
id: string,
request: OrganizationApiKeyRequest
): Promise<ApiKeyResponse> {
const r = await this.send(
"POST",
"/organizations/" + id + "/rotate-api-key",
request,
true,
true
);
return new ApiKeyResponse(r);
}
async postOrganizationSso(
id: string,
request: OrganizationSsoRequest
): Promise<OrganizationSsoResponse> {
const r = await this.send("POST", "/organizations/" + id + "/sso", request, true, true);
return new OrganizationSsoResponse(r);
}
async postOrganizationUpgrade(
id: string,
request: OrganizationUpgradeRequest
): Promise<PaymentResponse> {
const r = await this.send("POST", "/organizations/" + id + "/upgrade", request, true, true);
return new PaymentResponse(r);
}
async postOrganizationUpdateSubscription(
id: string,
request: OrganizationSubscriptionUpdateRequest
): Promise<void> {
return this.send("POST", "/organizations/" + id + "/subscription", request, true, false);
}
async postOrganizationSeat(id: string, request: SeatRequest): Promise<PaymentResponse> {
const r = await this.send("POST", "/organizations/" + id + "/seat", request, true, true);
return new PaymentResponse(r);
}
async postOrganizationStorage(id: string, request: StorageRequest): Promise<PaymentResponse> {
const r = await this.send("POST", "/organizations/" + id + "/storage", request, true, true);
return new PaymentResponse(r);
}
postOrganizationPayment(id: string, request: PaymentRequest): Promise<any> {
return this.send("POST", "/organizations/" + id + "/payment", request, true, false);
}
postOrganizationVerifyBank(id: string, request: VerifyBankRequest): Promise<any> {
return this.send("POST", "/organizations/" + id + "/verify-bank", request, true, false);
}
postOrganizationCancel(id: string): Promise<any> {
return this.send("POST", "/organizations/" + id + "/cancel", null, true, false);
}
postOrganizationReinstate(id: string): Promise<any> {
return this.send("POST", "/organizations/" + id + "/reinstate", null, true, false);
}
deleteOrganization(id: string, request: SecretVerificationRequest): Promise<any> {
return this.send("DELETE", "/organizations/" + id, request, true, false);
}
async getOrganizationKeys(id: string): Promise<OrganizationKeysResponse> {
const r = await this.send("GET", "/organizations/" + id + "/keys", null, true, true);
return new OrganizationKeysResponse(r);
}
async postOrganizationKeys(
id: string,
request: OrganizationKeysRequest
): Promise<OrganizationKeysResponse> {
const r = await this.send("POST", "/organizations/" + id + "/keys", request, true, true);
return new OrganizationKeysResponse(r);
}
// Provider APIs
async postProviderSetup(id: string, request: ProviderSetupRequest) {

View File

@@ -1,10 +1,7 @@
import { ApiService } from "../abstractions/api.service";
import { Utils } from "../misc/utils";
import { EncArrayBuffer } from "../models/domain/encArrayBuffer";
export class BitwardenFileUploadService {
constructor(private apiService: ApiService) {}
async upload(
encryptedFileName: string,
encryptedFileData: EncArrayBuffer,

View File

@@ -1,9 +1,15 @@
import { BroadcasterService as BroadcasterServiceAbstraction } from "../abstractions/broadcaster.service";
import {
BroadcasterService as BroadcasterServiceAbstraction,
MessageBase,
} from "../abstractions/broadcaster.service";
export class BroadcasterService implements BroadcasterServiceAbstraction {
subscribers: Map<string, (message: any) => any> = new Map<string, (message: any) => any>();
subscribers: Map<string, (message: MessageBase) => void> = new Map<
string,
(message: MessageBase) => void
>();
send(message: any, id?: string) {
send(message: MessageBase, id?: string) {
if (id != null) {
if (this.subscribers.has(id)) {
this.subscribers.get(id)(message);
@@ -16,7 +22,7 @@ export class BroadcasterService implements BroadcasterServiceAbstraction {
});
}
subscribe(id: string, messageCallback: (message: any) => any) {
subscribe(id: string, messageCallback: (message: MessageBase) => void) {
this.subscribers.set(id, messageCallback);
}

View File

@@ -1,4 +1,4 @@
import { Observable, Subject } from "rxjs";
import { concatMap, Observable, Subject } from "rxjs";
import {
EnvironmentService as EnvironmentServiceAbstraction,
@@ -22,9 +22,13 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
private scimUrl: string = null;
constructor(private stateService: StateService) {
this.stateService.activeAccount$.subscribe(async () => {
await this.setUrlsFromStorage();
});
this.stateService.activeAccount$
.pipe(
concatMap(async () => {
await this.setUrlsFromStorage();
})
)
.subscribe();
}
hasBaseUrl() {

View File

@@ -16,7 +16,7 @@ export class FileUploadService implements FileUploadServiceAbstraction {
constructor(private logService: LogService, private apiService: ApiService) {
this.azureFileUploadService = new AzureFileUploadService(logService);
this.bitwardenFileUploadService = new BitwardenFileUploadService(apiService);
this.bitwardenFileUploadService = new BitwardenFileUploadService();
}
async uploadSendFile(

View File

@@ -1,4 +1,4 @@
import { BehaviorSubject } from "rxjs";
import { BehaviorSubject, concatMap } from "rxjs";
import { CipherService } from "../../abstractions/cipher.service";
import { CryptoService } from "../../abstractions/crypto.service";
@@ -25,21 +25,25 @@ export class FolderService implements InternalFolderServiceAbstraction {
private cipherService: CipherService,
private stateService: StateService
) {
this.stateService.activeAccountUnlocked$.subscribe(async (unlocked) => {
if ((Utils.global as any).bitwardenContainerService == null) {
return;
}
this.stateService.activeAccountUnlocked$
.pipe(
concatMap(async (unlocked) => {
if (Utils.global.bitwardenContainerService == null) {
return;
}
if (!unlocked) {
this._folders.next([]);
this._folderViews.next([]);
return;
}
if (!unlocked) {
this._folders.next([]);
this._folderViews.next([]);
return;
}
const data = await this.stateService.getEncryptedFolders();
const data = await this.stateService.getEncryptedFolders();
await this.updateObservables(data);
});
await this.updateObservables(data);
})
)
.subscribe();
}
async clearCache(): Promise<void> {

View File

@@ -27,6 +27,7 @@ export class I18nService implements I18nServiceAbstraction {
["eo", "Esperanto"],
["es", "español"],
["et", "eesti"],
["eu", "euskara"],
["fa", "فارسی"],
["fi", "suomi"],
["fil", "Wikang Filipino"],

View File

@@ -5,7 +5,6 @@ import { CryptoService } from "../abstractions/crypto.service";
import { FolderService } from "../abstractions/folder/folder.service.abstraction";
import { I18nService } from "../abstractions/i18n.service";
import { ImportService as ImportServiceAbstraction } from "../abstractions/import.service";
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
import { CipherType } from "../enums/cipherType";
import {
featuredImportOptions,
@@ -93,7 +92,6 @@ export class ImportService implements ImportServiceAbstraction {
private apiService: ApiService,
private i18nService: I18nService,
private collectionService: CollectionService,
private platformUtilsService: PlatformUtilsService,
private cryptoService: CryptoService
) {}

View File

@@ -0,0 +1,263 @@
import { ApiService } from "../../abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "../../abstractions/organization/organization-api.service.abstraction";
import { OrganizationApiKeyType } from "../../enums/organizationApiKeyType";
import { ImportDirectoryRequest } from "../../models/request/importDirectoryRequest";
import { OrganizationSsoRequest } from "../../models/request/organization/organizationSsoRequest";
import { OrganizationApiKeyRequest } from "../../models/request/organizationApiKeyRequest";
import { OrganizationCreateRequest } from "../../models/request/organizationCreateRequest";
import { OrganizationKeysRequest } from "../../models/request/organizationKeysRequest";
import { OrganizationSubscriptionUpdateRequest } from "../../models/request/organizationSubscriptionUpdateRequest";
import { OrganizationTaxInfoUpdateRequest } from "../../models/request/organizationTaxInfoUpdateRequest";
import { OrganizationUpdateRequest } from "../../models/request/organizationUpdateRequest";
import { OrganizationUpgradeRequest } from "../../models/request/organizationUpgradeRequest";
import { PaymentRequest } from "../../models/request/paymentRequest";
import { SeatRequest } from "../../models/request/seatRequest";
import { SecretVerificationRequest } from "../../models/request/secretVerificationRequest";
import { StorageRequest } from "../../models/request/storageRequest";
import { VerifyBankRequest } from "../../models/request/verifyBankRequest";
import { ApiKeyResponse } from "../../models/response/apiKeyResponse";
import { BillingResponse } from "../../models/response/billingResponse";
import { ListResponse } from "../../models/response/listResponse";
import { OrganizationSsoResponse } from "../../models/response/organization/organizationSsoResponse";
import { OrganizationApiKeyInformationResponse } from "../../models/response/organizationApiKeyInformationResponse";
import { OrganizationAutoEnrollStatusResponse } from "../../models/response/organizationAutoEnrollStatusResponse";
import { OrganizationKeysResponse } from "../../models/response/organizationKeysResponse";
import { OrganizationResponse } from "../../models/response/organizationResponse";
import { OrganizationSubscriptionResponse } from "../../models/response/organizationSubscriptionResponse";
import { PaymentResponse } from "../../models/response/paymentResponse";
import { TaxInfoResponse } from "../../models/response/taxInfoResponse";
export class OrganizationApiService implements OrganizationApiServiceAbstraction {
constructor(private apiService: ApiService) {}
async get(id: string): Promise<OrganizationResponse> {
const r = await this.apiService.send("GET", "/organizations/" + id, null, true, true);
return new OrganizationResponse(r);
}
async getBilling(id: string): Promise<BillingResponse> {
const r = await this.apiService.send(
"GET",
"/organizations/" + id + "/billing",
null,
true,
true
);
return new BillingResponse(r);
}
async getSubscription(id: string): Promise<OrganizationSubscriptionResponse> {
const r = await this.apiService.send(
"GET",
"/organizations/" + id + "/subscription",
null,
true,
true
);
return new OrganizationSubscriptionResponse(r);
}
async getLicense(id: string, installationId: string): Promise<unknown> {
return this.apiService.send(
"GET",
"/organizations/" + id + "/license?installationId=" + installationId,
null,
true,
true
);
}
async getAutoEnrollStatus(identifier: string): Promise<OrganizationAutoEnrollStatusResponse> {
const r = await this.apiService.send(
"GET",
"/organizations/" + identifier + "/auto-enroll-status",
null,
true,
true
);
return new OrganizationAutoEnrollStatusResponse(r);
}
async create(request: OrganizationCreateRequest): Promise<OrganizationResponse> {
const r = await this.apiService.send("POST", "/organizations", request, true, true);
return new OrganizationResponse(r);
}
async createLicense(data: FormData): Promise<OrganizationResponse> {
const r = await this.apiService.send("POST", "/organizations/license", data, true, true);
return new OrganizationResponse(r);
}
async save(id: string, request: OrganizationUpdateRequest): Promise<OrganizationResponse> {
const r = await this.apiService.send("PUT", "/organizations/" + id, request, true, true);
return new OrganizationResponse(r);
}
async updatePayment(id: string, request: PaymentRequest): Promise<void> {
return this.apiService.send("POST", "/organizations/" + id + "/payment", request, true, false);
}
async upgrade(id: string, request: OrganizationUpgradeRequest): Promise<PaymentResponse> {
const r = await this.apiService.send(
"POST",
"/organizations/" + id + "/upgrade",
request,
true,
true
);
return new PaymentResponse(r);
}
async updateSubscription(
id: string,
request: OrganizationSubscriptionUpdateRequest
): Promise<void> {
return this.apiService.send(
"POST",
"/organizations/" + id + "/subscription",
request,
true,
false
);
}
async updateSeats(id: string, request: SeatRequest): Promise<PaymentResponse> {
const r = await this.apiService.send(
"POST",
"/organizations/" + id + "/seat",
request,
true,
true
);
return new PaymentResponse(r);
}
async updateStorage(id: string, request: StorageRequest): Promise<PaymentResponse> {
const r = await this.apiService.send(
"POST",
"/organizations/" + id + "/storage",
request,
true,
true
);
return new PaymentResponse(r);
}
async verifyBank(id: string, request: VerifyBankRequest): Promise<void> {
return this.apiService.send(
"POST",
"/organizations/" + id + "/verify-bank",
request,
true,
false
);
}
async cancel(id: string): Promise<void> {
return this.apiService.send("POST", "/organizations/" + id + "/cancel", null, true, false);
}
async reinstate(id: string): Promise<void> {
return this.apiService.send("POST", "/organizations/" + id + "/reinstate", null, true, false);
}
async leave(id: string): Promise<void> {
return this.apiService.send("POST", "/organizations/" + id + "/leave", null, true, false);
}
async delete(id: string, request: SecretVerificationRequest): Promise<void> {
return this.apiService.send("DELETE", "/organizations/" + id, request, true, false);
}
async updateLicense(id: string, data: FormData): Promise<void> {
return this.apiService.send("POST", "/organizations/" + id + "/license", data, true, false);
}
async importDirectory(organizationId: string, request: ImportDirectoryRequest): Promise<void> {
return this.apiService.send(
"POST",
"/organizations/" + organizationId + "/import",
request,
true,
false
);
}
async getOrCreateApiKey(id: string, request: OrganizationApiKeyRequest): Promise<ApiKeyResponse> {
const r = await this.apiService.send(
"POST",
"/organizations/" + id + "/api-key",
request,
true,
true
);
return new ApiKeyResponse(r);
}
async getApiKeyInformation(
id: string,
organizationApiKeyType: OrganizationApiKeyType = null
): Promise<ListResponse<OrganizationApiKeyInformationResponse>> {
const uri =
organizationApiKeyType === null
? "/organizations/" + id + "/api-key-information"
: "/organizations/" + id + "/api-key-information/" + organizationApiKeyType;
const r = await this.apiService.send("GET", uri, null, true, true);
return new ListResponse(r, OrganizationApiKeyInformationResponse);
}
async rotateApiKey(id: string, request: OrganizationApiKeyRequest): Promise<ApiKeyResponse> {
const r = await this.apiService.send(
"POST",
"/organizations/" + id + "/rotate-api-key",
request,
true,
true
);
return new ApiKeyResponse(r);
}
async getTaxInfo(id: string): Promise<TaxInfoResponse> {
const r = await this.apiService.send("GET", "/organizations/" + id + "/tax", null, true, true);
return new TaxInfoResponse(r);
}
async updateTaxInfo(id: string, request: OrganizationTaxInfoUpdateRequest): Promise<void> {
return this.apiService.send("PUT", "/organizations/" + id + "/tax", request, true, false);
}
async getKeys(id: string): Promise<OrganizationKeysResponse> {
const r = await this.apiService.send("GET", "/organizations/" + id + "/keys", null, true, true);
return new OrganizationKeysResponse(r);
}
async updateKeys(
id: string,
request: OrganizationKeysRequest
): Promise<OrganizationKeysResponse> {
const r = await this.apiService.send(
"POST",
"/organizations/" + id + "/keys",
request,
true,
true
);
return new OrganizationKeysResponse(r);
}
async getSso(id: string): Promise<OrganizationSsoResponse> {
const r = await this.apiService.send("GET", "/organizations/" + id + "/sso", null, true, true);
return new OrganizationSsoResponse(r);
}
async updateSso(id: string, request: OrganizationSsoRequest): Promise<OrganizationSsoResponse> {
const r = await this.apiService.send(
"POST",
"/organizations/" + id + "/sso",
request,
true,
true
);
return new OrganizationSsoResponse(r);
}
}

View File

@@ -1,4 +1,4 @@
import { BehaviorSubject } from "rxjs";
import { BehaviorSubject, concatMap } from "rxjs";
import { LogService } from "../abstractions/log.service";
import { StateService as StateServiceAbstraction } from "../abstractions/state.service";
@@ -76,18 +76,22 @@ export class StateService<
protected useAccountCache: boolean = true
) {
// If the account gets changed, verify the new account is unlocked
this.activeAccountSubject.subscribe(async (userId) => {
if (userId == null && this.activeAccountUnlockedSubject.getValue() == false) {
return;
} else if (userId == null) {
this.activeAccountUnlockedSubject.next(false);
}
this.activeAccountSubject
.pipe(
concatMap(async (userId) => {
if (userId == null && this.activeAccountUnlockedSubject.getValue() == false) {
return;
} else if (userId == null) {
this.activeAccountUnlockedSubject.next(false);
}
// FIXME: This should be refactored into AuthService or a similar service,
// as checking for the existance of the crypto key is a low level
// implementation detail.
this.activeAccountUnlockedSubject.next((await this.getCryptoMasterKey()) != null);
});
// FIXME: This should be refactored into AuthService or a similar service,
// as checking for the existance of the crypto key is a low level
// implementation detail.
this.activeAccountUnlockedSubject.next((await this.getCryptoMasterKey()) != null);
})
)
.subscribe();
}
async init(): Promise<void> {

View File

@@ -2,6 +2,13 @@ import { ApiService } from "../abstractions/api.service";
import { CryptoService } from "../abstractions/crypto.service";
import { StateService } from "../abstractions/state.service";
import { UsernameGenerationService as BaseUsernameGenerationService } from "../abstractions/usernameGeneration.service";
import { AnonAddyForwarder } from "../emailForwarders/anonAddyForwarder";
import { DuckDuckGoForwarder } from "../emailForwarders/duckDuckGoForwarder";
import { FastmailForwarder } from "../emailForwarders/fastmailForwarder";
import { FirefoxRelayForwarder } from "../emailForwarders/firefoxRelayForwarder";
import { Forwarder } from "../emailForwarders/forwarder";
import { ForwarderOptions } from "../emailForwarders/forwarderOptions";
import { SimpleLoginForwarder } from "../emailForwarders/simpleLoginForwarder";
import { EEFLongWordList } from "../misc/wordlist";
const DefaultOptions = {
@@ -108,33 +115,32 @@ export class UsernameGenerationService implements BaseUsernameGenerationService
return null;
}
let forwarder: Forwarder = null;
const forwarderOptions = new ForwarderOptions();
forwarderOptions.website = o.website;
if (o.forwardedService === "simplelogin") {
if (o.forwardedSimpleLoginApiKey == null || o.forwardedSimpleLoginApiKey === "") {
return null;
}
return this.generateSimpleLoginAlias(o.forwardedSimpleLoginApiKey, o.website);
forwarder = new SimpleLoginForwarder();
forwarderOptions.apiKey = o.forwardedSimpleLoginApiKey;
} else if (o.forwardedService === "anonaddy") {
if (
o.forwardedAnonAddyApiToken == null ||
o.forwardedAnonAddyApiToken === "" ||
o.forwardedAnonAddyDomain == null ||
o.forwardedAnonAddyDomain == ""
) {
return null;
}
return this.generateAnonAddyAlias(
o.forwardedAnonAddyApiToken,
o.forwardedAnonAddyDomain,
o.website
);
forwarder = new AnonAddyForwarder();
forwarderOptions.apiKey = o.forwardedAnonAddyApiToken;
forwarderOptions.anonaddy.domain = o.forwardedAnonAddyDomain;
} else if (o.forwardedService === "firefoxrelay") {
if (o.forwardedFirefoxApiToken == null || o.forwardedFirefoxApiToken === "") {
return null;
}
return this.generateFirefoxRelayAlias(o.forwardedFirefoxApiToken, o.website);
forwarder = new FirefoxRelayForwarder();
forwarderOptions.apiKey = o.forwardedFirefoxApiToken;
} else if (o.forwardedService === "fastmail") {
forwarder = new FastmailForwarder();
forwarderOptions.apiKey = o.forwardedFastmailApiToken;
} else if (o.forwardedService === "duckduckgo") {
forwarder = new DuckDuckGoForwarder();
forwarderOptions.apiKey = o.forwardedDuckDuckGoToken;
}
return null;
if (forwarder == null) {
return null;
}
return forwarder.generate(this.apiService, forwarderOptions);
}
async getOptions(): Promise<any> {
@@ -168,110 +174,4 @@ export class UsernameGenerationService implements BaseUsernameGenerationService
? number
: new Array(width - number.length + 1).join("0") + number;
}
private async generateSimpleLoginAlias(apiKey: string, website: string): Promise<string> {
if (apiKey == null || apiKey === "") {
throw "Invalid SimpleLogin API key.";
}
const requestInit: RequestInit = {
redirect: "manual",
cache: "no-store",
method: "POST",
headers: new Headers({
Authentication: apiKey,
"Content-Type": "application/json",
}),
};
let url = "https://app.simplelogin.io/api/alias/random/new";
if (website != null) {
url += "?hostname=" + website;
}
requestInit.body = JSON.stringify({
note: (website != null ? "Website: " + website + ". " : "") + "Generated by Bitwarden.",
});
const request = new Request(url, requestInit);
const response = await this.apiService.nativeFetch(request);
if (response.status === 200 || response.status === 201) {
const json = await response.json();
return json.alias;
}
if (response.status === 401) {
throw "Invalid SimpleLogin API key.";
}
try {
const json = await response.json();
if (json?.error != null) {
throw "SimpleLogin error:" + json.error;
}
} catch {
// Do nothing...
}
throw "Unknown SimpleLogin error occurred.";
}
private async generateAnonAddyAlias(
apiToken: string,
domain: string,
websiteNote: string
): Promise<string> {
if (apiToken == null || apiToken === "") {
throw "Invalid AnonAddy API token.";
}
const requestInit: RequestInit = {
redirect: "manual",
cache: "no-store",
method: "POST",
headers: new Headers({
Authorization: "Bearer " + apiToken,
"Content-Type": "application/json",
}),
};
const url = "https://app.anonaddy.com/api/v1/aliases";
requestInit.body = JSON.stringify({
domain: domain,
description:
(websiteNote != null ? "Website: " + websiteNote + ". " : "") + "Generated by Bitwarden.",
});
const request = new Request(url, requestInit);
const response = await this.apiService.nativeFetch(request);
if (response.status === 200 || response.status === 201) {
const json = await response.json();
return json?.data?.email;
}
if (response.status === 401) {
throw "Invalid AnonAddy API token.";
}
throw "Unknown AnonAddy error occurred.";
}
private async generateFirefoxRelayAlias(apiToken: string, website: string): Promise<string> {
if (apiToken == null || apiToken === "") {
throw "Invalid Firefox Relay API token.";
}
const requestInit: RequestInit = {
redirect: "manual",
cache: "no-store",
method: "POST",
headers: new Headers({
Authorization: "Token " + apiToken,
"Content-Type": "application/json",
}),
};
const url = "https://relay.firefox.com/api/v1/relayaddresses/";
requestInit.body = JSON.stringify({
enabled: true,
generated_for: website,
description: (website != null ? website + " - " : "") + "Generated by Bitwarden.",
});
const request = new Request(url, requestInit);
const response = await this.apiService.nativeFetch(request);
if (response.status === 200 || response.status === 201) {
const json = await response.json();
return json?.full_address;
}
if (response.status === 401) {
throw "Invalid Firefox Relay API token.";
}
throw "Unknown Firefox Relay error occurred.";
}
}

View File

@@ -62,7 +62,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
}
}
async lock(allowSoftLock = false, userId?: string): Promise<void> {
async lock(userId?: string): Promise<void> {
const authed = await this.stateService.getIsAuthenticated({ userId: userId });
if (!authed) {
return;
@@ -200,6 +200,6 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
private async executeTimeoutAction(userId: string): Promise<void> {
const timeoutAction = await this.stateService.getVaultTimeoutAction({ userId: userId });
timeoutAction === "logOut" ? await this.logOut(userId) : await this.lock(true, userId);
timeoutAction === "logOut" ? await this.logOut(userId) : await this.lock(userId);
}
}