mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
Merge branch 'master' into feature/org-admin-refresh
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Directive, EventEmitter, OnInit, Output } from "@angular/core";
|
||||
import { UntypedFormBuilder } from "@angular/forms";
|
||||
import { Directive, EventEmitter, OnDestroy, OnInit, Output } from "@angular/core";
|
||||
import { UntypedFormBuilder, Validators } from "@angular/forms";
|
||||
import { merge, takeUntil, Subject, startWith } from "rxjs";
|
||||
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
@@ -10,19 +11,25 @@ import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
import { EncryptedExportType } from "@bitwarden/common/enums/encryptedExportType";
|
||||
import { EventType } from "@bitwarden/common/enums/eventType";
|
||||
import { PolicyType } from "@bitwarden/common/enums/policyType";
|
||||
|
||||
@Directive()
|
||||
export class ExportComponent implements OnInit {
|
||||
export class ExportComponent implements OnInit, OnDestroy {
|
||||
@Output() onSaved = new EventEmitter();
|
||||
|
||||
formPromise: Promise<string>;
|
||||
disabledByPolicy = false;
|
||||
showFilePassword: boolean;
|
||||
showConfirmFilePassword: boolean;
|
||||
|
||||
exportForm = this.formBuilder.group({
|
||||
format: ["json"],
|
||||
secret: [""],
|
||||
filePassword: ["", Validators.required],
|
||||
confirmFilePassword: ["", Validators.required],
|
||||
fileEncryptionType: [EncryptedExportType.AccountEncrypted],
|
||||
});
|
||||
|
||||
formatOptions = [
|
||||
@@ -31,6 +38,8 @@ export class ExportComponent implements OnInit {
|
||||
{ name: ".json (Encrypted)", value: "encrypted_json" },
|
||||
];
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
protected cryptoService: CryptoService,
|
||||
protected i18nService: I18nService,
|
||||
@@ -47,6 +56,18 @@ export class ExportComponent implements OnInit {
|
||||
|
||||
async ngOnInit() {
|
||||
await this.checkExportDisabled();
|
||||
|
||||
merge(
|
||||
this.exportForm.get("format").valueChanges,
|
||||
this.exportForm.get("fileEncryptionType").valueChanges
|
||||
)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.pipe(startWith(0))
|
||||
.subscribe(() => this.adjustValidators());
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
}
|
||||
|
||||
async checkExportDisabled() {
|
||||
@@ -62,6 +83,20 @@ export class ExportComponent implements OnInit {
|
||||
return this.format === "encrypted_json";
|
||||
}
|
||||
|
||||
protected async doExport() {
|
||||
try {
|
||||
this.formPromise = this.getExportData();
|
||||
const data = await this.formPromise;
|
||||
this.downloadFile(data);
|
||||
this.saved();
|
||||
await this.collectEvent();
|
||||
this.exportForm.get("secret").setValue("");
|
||||
this.exportForm.clearValidators();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (this.disabledByPolicy) {
|
||||
this.platformUtilsService.showToast(
|
||||
@@ -76,25 +111,15 @@ export class ExportComponent implements OnInit {
|
||||
if (!acceptedWarning) {
|
||||
return;
|
||||
}
|
||||
|
||||
const secret = this.exportForm.get("secret").value;
|
||||
|
||||
try {
|
||||
await this.userVerificationService.verifyUser(secret);
|
||||
} catch (e) {
|
||||
this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.formPromise = this.getExportData();
|
||||
const data = await this.formPromise;
|
||||
this.downloadFile(data);
|
||||
this.saved();
|
||||
await this.collectEvent();
|
||||
this.exportForm.get("secret").setValue("");
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
this.doExport();
|
||||
}
|
||||
|
||||
async warningDialog() {
|
||||
@@ -126,7 +151,14 @@ export class ExportComponent implements OnInit {
|
||||
}
|
||||
|
||||
protected getExportData() {
|
||||
return this.exportService.getExport(this.format);
|
||||
if (
|
||||
this.format === "encrypted_json" &&
|
||||
this.fileEncryptionType === EncryptedExportType.FileEncrypted
|
||||
) {
|
||||
return this.exportService.getPasswordProtectedExport(this.filePassword);
|
||||
} else {
|
||||
return this.exportService.getExport(this.format, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected getFileName(prefix?: string) {
|
||||
@@ -150,6 +182,41 @@ export class ExportComponent implements OnInit {
|
||||
return this.exportForm.get("format").value;
|
||||
}
|
||||
|
||||
get filePassword() {
|
||||
return this.exportForm.get("filePassword").value;
|
||||
}
|
||||
|
||||
get confirmFilePassword() {
|
||||
return this.exportForm.get("confirmFilePassword").value;
|
||||
}
|
||||
|
||||
get fileEncryptionType() {
|
||||
return this.exportForm.get("fileEncryptionType").value;
|
||||
}
|
||||
|
||||
toggleFilePassword() {
|
||||
this.showFilePassword = !this.showFilePassword;
|
||||
document.getElementById("filePassword").focus();
|
||||
}
|
||||
|
||||
toggleConfirmFilePassword() {
|
||||
this.showConfirmFilePassword = !this.showConfirmFilePassword;
|
||||
document.getElementById("confirmFilePassword").focus();
|
||||
}
|
||||
|
||||
adjustValidators() {
|
||||
this.exportForm.get("confirmFilePassword").reset();
|
||||
this.exportForm.get("filePassword").reset();
|
||||
|
||||
if (this.encryptedFormat && this.fileEncryptionType == EncryptedExportType.FileEncrypted) {
|
||||
this.exportForm.controls.filePassword.enable();
|
||||
this.exportForm.controls.confirmFilePassword.enable();
|
||||
} else {
|
||||
this.exportForm.controls.filePassword.disable();
|
||||
this.exportForm.controls.confirmFilePassword.disable();
|
||||
}
|
||||
}
|
||||
|
||||
private downloadFile(csv: string): void {
|
||||
const fileName = this.getFileName();
|
||||
this.fileDownloadService.download({
|
||||
|
||||
@@ -74,11 +74,13 @@ export class GeneratorComponent implements OnInit {
|
||||
{ name: "SimpleLogin", value: "simplelogin" },
|
||||
{ name: "AnonAddy", value: "anonaddy" },
|
||||
{ name: "Firefox Relay", value: "firefoxrelay" },
|
||||
// { name: "FastMail", value: "fastmail" },
|
||||
{ name: "Fastmail", value: "fastmail" },
|
||||
{ name: "DuckDuckGo", value: "duckduckgo" },
|
||||
];
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
// eslint-disable-next-line rxjs/no-async-subscribe
|
||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||
const passwordOptionsResponse = await this.passwordGenerationService.getOptions();
|
||||
this.passwordOptions = passwordOptionsResponse[0];
|
||||
|
||||
@@ -58,6 +58,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
// eslint-disable-next-line rxjs/no-async-subscribe
|
||||
this.activeAccountSubscription = this.stateService.activeAccount$.subscribe(async () => {
|
||||
await this.load();
|
||||
});
|
||||
@@ -68,136 +69,11 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (this.pinLock && (this.pin == null || this.pin === "")) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("pinRequired")
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.pinLock && (this.masterPassword == null || this.masterPassword === "")) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("masterPassRequired")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const kdf = await this.stateService.getKdfType();
|
||||
const kdfIterations = await this.stateService.getKdfIterations();
|
||||
|
||||
if (this.pinLock) {
|
||||
let failed = true;
|
||||
try {
|
||||
if (this.pinSet[0]) {
|
||||
const key = await this.cryptoService.makeKeyFromPin(
|
||||
this.pin,
|
||||
this.email,
|
||||
kdf,
|
||||
kdfIterations,
|
||||
await this.stateService.getDecryptedPinProtected()
|
||||
);
|
||||
const encKey = await this.cryptoService.getEncKey(key);
|
||||
const protectedPin = await this.stateService.getProtectedPin();
|
||||
const decPin = await this.cryptoService.decryptToUtf8(
|
||||
new EncString(protectedPin),
|
||||
encKey
|
||||
);
|
||||
failed = decPin !== this.pin;
|
||||
if (!failed) {
|
||||
await this.setKeyAndContinue(key);
|
||||
}
|
||||
} else {
|
||||
const key = await this.cryptoService.makeKeyFromPin(
|
||||
this.pin,
|
||||
this.email,
|
||||
kdf,
|
||||
kdfIterations
|
||||
);
|
||||
failed = false;
|
||||
await this.setKeyAndContinue(key);
|
||||
}
|
||||
} catch {
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
this.invalidPinAttempts++;
|
||||
if (this.invalidPinAttempts >= 5) {
|
||||
this.messagingService.send("logout");
|
||||
return;
|
||||
}
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("invalidPin")
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const key = await this.cryptoService.makeKey(
|
||||
this.masterPassword,
|
||||
this.email,
|
||||
kdf,
|
||||
kdfIterations
|
||||
);
|
||||
const storedKeyHash = await this.cryptoService.getKeyHash();
|
||||
|
||||
let passwordValid = false;
|
||||
|
||||
if (storedKeyHash != null) {
|
||||
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, key);
|
||||
} else {
|
||||
const request = new SecretVerificationRequest();
|
||||
const serverKeyHash = await this.cryptoService.hashPassword(
|
||||
this.masterPassword,
|
||||
key,
|
||||
HashPurpose.ServerAuthorization
|
||||
);
|
||||
request.masterPasswordHash = serverKeyHash;
|
||||
try {
|
||||
this.formPromise = this.apiService.postAccountVerifyPassword(request);
|
||||
await this.formPromise;
|
||||
passwordValid = true;
|
||||
const localKeyHash = await this.cryptoService.hashPassword(
|
||||
this.masterPassword,
|
||||
key,
|
||||
HashPurpose.LocalAuthorization
|
||||
);
|
||||
await this.cryptoService.setKeyHash(localKeyHash);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (passwordValid) {
|
||||
if (this.pinSet[0]) {
|
||||
const protectedPin = await this.stateService.getProtectedPin();
|
||||
const encKey = await this.cryptoService.getEncKey(key);
|
||||
const decPin = await this.cryptoService.decryptToUtf8(
|
||||
new EncString(protectedPin),
|
||||
encKey
|
||||
);
|
||||
const pinKey = await this.cryptoService.makePinKey(
|
||||
decPin,
|
||||
this.email,
|
||||
kdf,
|
||||
kdfIterations
|
||||
);
|
||||
await this.stateService.setDecryptedPinProtected(
|
||||
await this.cryptoService.encrypt(key.key, pinKey)
|
||||
);
|
||||
}
|
||||
await this.setKeyAndContinue(key);
|
||||
} else {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("invalidMasterPassword")
|
||||
);
|
||||
}
|
||||
return await this.handlePinRequiredUnlock();
|
||||
}
|
||||
|
||||
await this.handleMasterPasswordRequiredUnlock();
|
||||
}
|
||||
|
||||
async logOut() {
|
||||
@@ -236,6 +112,138 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private async handlePinRequiredUnlock() {
|
||||
if (this.pin == null || this.pin === "") {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("pinRequired")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
return await this.doUnlockWithPin();
|
||||
}
|
||||
|
||||
private async doUnlockWithPin() {
|
||||
let failed = true;
|
||||
try {
|
||||
const kdf = await this.stateService.getKdfType();
|
||||
const kdfIterations = await this.stateService.getKdfIterations();
|
||||
if (this.pinSet[0]) {
|
||||
const key = await this.cryptoService.makeKeyFromPin(
|
||||
this.pin,
|
||||
this.email,
|
||||
kdf,
|
||||
kdfIterations,
|
||||
await this.stateService.getDecryptedPinProtected()
|
||||
);
|
||||
const encKey = await this.cryptoService.getEncKey(key);
|
||||
const protectedPin = await this.stateService.getProtectedPin();
|
||||
const decPin = await this.cryptoService.decryptToUtf8(new EncString(protectedPin), encKey);
|
||||
failed = decPin !== this.pin;
|
||||
if (!failed) {
|
||||
await this.setKeyAndContinue(key);
|
||||
}
|
||||
} else {
|
||||
const key = await this.cryptoService.makeKeyFromPin(
|
||||
this.pin,
|
||||
this.email,
|
||||
kdf,
|
||||
kdfIterations
|
||||
);
|
||||
failed = false;
|
||||
await this.setKeyAndContinue(key);
|
||||
}
|
||||
} catch {
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
this.invalidPinAttempts++;
|
||||
if (this.invalidPinAttempts >= 5) {
|
||||
this.messagingService.send("logout");
|
||||
return;
|
||||
}
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("invalidPin")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleMasterPasswordRequiredUnlock() {
|
||||
if (this.masterPassword == null || this.masterPassword === "") {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("masterPassRequired")
|
||||
);
|
||||
return;
|
||||
}
|
||||
await this.doUnlockWithMasterPassword();
|
||||
}
|
||||
|
||||
private async doUnlockWithMasterPassword() {
|
||||
const kdf = await this.stateService.getKdfType();
|
||||
const kdfIterations = await this.stateService.getKdfIterations();
|
||||
|
||||
const key = await this.cryptoService.makeKey(
|
||||
this.masterPassword,
|
||||
this.email,
|
||||
kdf,
|
||||
kdfIterations
|
||||
);
|
||||
const storedKeyHash = await this.cryptoService.getKeyHash();
|
||||
|
||||
let passwordValid = false;
|
||||
|
||||
if (storedKeyHash != null) {
|
||||
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, key);
|
||||
} else {
|
||||
const request = new SecretVerificationRequest();
|
||||
const serverKeyHash = await this.cryptoService.hashPassword(
|
||||
this.masterPassword,
|
||||
key,
|
||||
HashPurpose.ServerAuthorization
|
||||
);
|
||||
request.masterPasswordHash = serverKeyHash;
|
||||
try {
|
||||
this.formPromise = this.apiService.postAccountVerifyPassword(request);
|
||||
await this.formPromise;
|
||||
passwordValid = true;
|
||||
const localKeyHash = await this.cryptoService.hashPassword(
|
||||
this.masterPassword,
|
||||
key,
|
||||
HashPurpose.LocalAuthorization
|
||||
);
|
||||
await this.cryptoService.setKeyHash(localKeyHash);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!passwordValid) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorOccurred"),
|
||||
this.i18nService.t("invalidMasterPassword")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.pinSet[0]) {
|
||||
const protectedPin = await this.stateService.getProtectedPin();
|
||||
const encKey = await this.cryptoService.getEncKey(key);
|
||||
const decPin = await this.cryptoService.decryptToUtf8(new EncString(protectedPin), encKey);
|
||||
const pinKey = await this.cryptoService.makePinKey(decPin, this.email, kdf, kdfIterations);
|
||||
await this.stateService.setDecryptedPinProtected(
|
||||
await this.cryptoService.encrypt(key.key, pinKey)
|
||||
);
|
||||
}
|
||||
await this.setKeyAndContinue(key);
|
||||
}
|
||||
private async setKeyAndContinue(key: SymmetricCryptoKey) {
|
||||
await this.cryptoService.setKey(key);
|
||||
await this.doContinue();
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Router } from "@angular/router";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { SyncService } from "@bitwarden/common/abstractions/sync.service";
|
||||
@@ -11,7 +12,7 @@ import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
|
||||
@Directive()
|
||||
export class RemovePasswordComponent implements OnInit {
|
||||
actionPromise: Promise<any>;
|
||||
actionPromise: Promise<void | boolean>;
|
||||
continuing = false;
|
||||
leaving = false;
|
||||
|
||||
@@ -26,7 +27,8 @@ export class RemovePasswordComponent implements OnInit {
|
||||
private syncService: SyncService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
private keyConnectorService: KeyConnectorService
|
||||
private keyConnectorService: KeyConnectorService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -68,7 +70,7 @@ export class RemovePasswordComponent implements OnInit {
|
||||
|
||||
try {
|
||||
this.leaving = true;
|
||||
this.actionPromise = this.apiService.postLeaveOrganization(this.organization.id).then(() => {
|
||||
this.actionPromise = this.organizationApiService.leave(this.organization.id).then(() => {
|
||||
return this.syncService.fullSync(true);
|
||||
});
|
||||
await this.actionPromise;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction";
|
||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction";
|
||||
@@ -32,7 +33,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
orgId: string;
|
||||
resetPasswordAutoEnroll = false;
|
||||
|
||||
onSuccessfulChangePassword: () => Promise<any>;
|
||||
onSuccessfulChangePassword: () => Promise<void>;
|
||||
successRoute = "vault";
|
||||
|
||||
constructor(
|
||||
@@ -47,7 +48,8 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
private apiService: ApiService,
|
||||
private syncService: SyncService,
|
||||
private route: ActivatedRoute,
|
||||
stateService: StateService
|
||||
stateService: StateService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
@@ -64,6 +66,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
await this.syncService.fullSync(true);
|
||||
this.syncLoading = false;
|
||||
|
||||
// eslint-disable-next-line rxjs/no-async-subscribe
|
||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||
if (qParams.identifier != null) {
|
||||
this.identifier = qParams.identifier;
|
||||
@@ -73,7 +76,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
// Automatic Enrollment Detection
|
||||
if (this.identifier != null) {
|
||||
try {
|
||||
const response = await this.apiService.getOrganizationAutoEnrollStatus(this.identifier);
|
||||
const response = await this.organizationApiService.getAutoEnrollStatus(this.identifier);
|
||||
this.orgId = response.id;
|
||||
this.resetPasswordAutoEnroll = response.resetPasswordEnabled;
|
||||
this.enforcedPolicyOptions =
|
||||
@@ -113,7 +116,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
.setPassword(request)
|
||||
.then(async () => {
|
||||
await this.onSetPasswordSuccess(key, encKey, keys);
|
||||
return this.apiService.getOrganizationKeys(this.orgId);
|
||||
return this.organizationApiService.getKeys(this.orgId);
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response == null) {
|
||||
|
||||
@@ -66,6 +66,7 @@ export class VaultTimeoutInputComponent implements ControlValueAccessor, Validat
|
||||
this.validatorChange();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line rxjs/no-async-subscribe
|
||||
this.form.valueChanges.subscribe(async (value) => {
|
||||
this.onChange(this.getVaultTimeout(value));
|
||||
});
|
||||
|
||||
@@ -53,6 +53,7 @@ export class SsoComponent {
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
// eslint-disable-next-line rxjs/no-async-subscribe
|
||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||
if (qParams.code != null && qParams.state != null) {
|
||||
const codeVerifier = await this.stateService.getSsoCodeVerifier();
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Directive } from "@angular/core";
|
||||
import { FormBuilder, FormControl } from "@angular/forms";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction";
|
||||
|
||||
import { ModalConfig } from "../services/modal.service";
|
||||
|
||||
import { ModalRef } from "./modal/modal.ref";
|
||||
|
||||
/**
|
||||
* Used to verify the user's identity (using their master password or email-based OTP for Key Connector users). You can customize all of the text in the modal.
|
||||
*/
|
||||
@Directive()
|
||||
export class UserVerificationPromptComponent {
|
||||
confirmDescription = this.config.data.confirmDescription;
|
||||
confirmButtonText = this.config.data.confirmButtonText;
|
||||
modalTitle = this.config.data.modalTitle;
|
||||
secret = new FormControl();
|
||||
|
||||
constructor(
|
||||
private modalRef: ModalRef,
|
||||
protected config: ModalConfig,
|
||||
protected userVerificationService: UserVerificationService,
|
||||
private formBuilder: FormBuilder,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService
|
||||
) {}
|
||||
|
||||
async submit() {
|
||||
try {
|
||||
//Incorrect secret will throw an invalid password error.
|
||||
await this.userVerificationService.verifyUser(this.secret.value);
|
||||
} catch (e) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("error"),
|
||||
this.i18nService.t("invalidMasterPassword")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.modalRef.close(true);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import { Verification } from "@bitwarden/common/types/verification";
|
||||
]),
|
||||
],
|
||||
})
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class UserVerificationComponent implements ControlValueAccessor, OnInit {
|
||||
usesKeyConnector = false;
|
||||
disableRequestOTP = false;
|
||||
@@ -48,6 +49,7 @@ export class UserVerificationComponent implements ControlValueAccessor, OnInit {
|
||||
this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||
this.processChanges(this.secret.value);
|
||||
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
this.secret.valueChanges.subscribe((secret: string) => this.processChanges(secret));
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { OrganizationService as OrganizationServiceAbstraction } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction";
|
||||
import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
@@ -77,6 +78,7 @@ import { FormValidationErrorsService } from "@bitwarden/common/services/formVali
|
||||
import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service";
|
||||
import { NotificationsService } from "@bitwarden/common/services/notifications.service";
|
||||
import { OrganizationService } from "@bitwarden/common/services/organization.service";
|
||||
import { OrganizationApiService } from "@bitwarden/common/services/organization/organization-api.service";
|
||||
import { PasswordGenerationService } from "@bitwarden/common/services/passwordGeneration.service";
|
||||
import { PolicyApiService } from "@bitwarden/common/services/policy/policy-api.service";
|
||||
import { PolicyService } from "@bitwarden/common/services/policy/policy.service";
|
||||
@@ -388,6 +390,7 @@ export const LOG_MAC_FAILURES = new InjectionToken<string>("LOG_MAC_FAILURES");
|
||||
CipherServiceAbstraction,
|
||||
ApiServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
CryptoFunctionServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -513,6 +516,11 @@ export const LOG_MAC_FAILURES = new InjectionToken<string>("LOG_MAC_FAILURES");
|
||||
useClass: UserVerificationApiService,
|
||||
deps: [ApiServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: OrganizationApiServiceAbstraction,
|
||||
useClass: OrganizationApiService,
|
||||
deps: [ApiServiceAbstraction],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class JslibServicesModule {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core";
|
||||
import { Component, EventEmitter, Input, OnChanges, Output } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||
@@ -15,9 +15,10 @@ export interface PasswordColorText {
|
||||
export class PasswordStrengthComponent implements OnChanges {
|
||||
@Input() showText = false;
|
||||
@Input() email: string;
|
||||
@Input() password: string;
|
||||
@Input() name: string;
|
||||
|
||||
@Input() set password(value: string) {
|
||||
this.updatePasswordStrength(value);
|
||||
}
|
||||
@Output() passwordStrengthResult = new EventEmitter<any>();
|
||||
@Output() passwordScoreColor = new EventEmitter<PasswordColorText>();
|
||||
|
||||
@@ -61,10 +62,8 @@ export class PasswordStrengthComponent implements OnChanges {
|
||||
private passwordGenerationService: PasswordGenerationService
|
||||
) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
ngOnChanges(): void {
|
||||
this.masterPasswordStrengthTimeout = setTimeout(() => {
|
||||
this.updatePasswordStrength(changes.password?.currentValue);
|
||||
|
||||
this.scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||
|
||||
switch (this.masterPasswordScore) {
|
||||
@@ -87,7 +86,7 @@ export class PasswordStrengthComponent implements OnChanges {
|
||||
}
|
||||
|
||||
this.setPasswordScoreText(this.color, this.text);
|
||||
}, 100);
|
||||
}, 300);
|
||||
}
|
||||
|
||||
updatePasswordStrength(password: string) {
|
||||
@@ -102,7 +101,6 @@ export class PasswordStrengthComponent implements OnChanges {
|
||||
this.getPasswordStrengthUserInput()
|
||||
);
|
||||
this.passwordStrengthResult.emit(strengthResult);
|
||||
|
||||
this.masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
||||
}
|
||||
|
||||
@@ -110,7 +108,7 @@ export class PasswordStrengthComponent implements OnChanges {
|
||||
let userInput: string[] = [];
|
||||
const email = this.email;
|
||||
const name = this.name;
|
||||
const atPosition = email.indexOf("@");
|
||||
const atPosition = email?.indexOf("@");
|
||||
if (atPosition > -1) {
|
||||
userInput = userInput.concat(
|
||||
email
|
||||
|
||||
@@ -6,7 +6,6 @@ import { CollectionService } from "@bitwarden/common/abstractions/collection.ser
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { BitwardenPasswordProtectedImporter } from "@bitwarden/common/importers/bitwardenPasswordProtectedImporter";
|
||||
import { Importer } from "@bitwarden/common/importers/importer";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
@@ -19,7 +18,6 @@ describe("ImportService", () => {
|
||||
let apiService: SubstituteOf<ApiService>;
|
||||
let i18nService: SubstituteOf<I18nService>;
|
||||
let collectionService: SubstituteOf<CollectionService>;
|
||||
let platformUtilsService: SubstituteOf<PlatformUtilsService>;
|
||||
let cryptoService: SubstituteOf<CryptoService>;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -28,7 +26,6 @@ describe("ImportService", () => {
|
||||
apiService = Substitute.for<ApiService>();
|
||||
i18nService = Substitute.for<I18nService>();
|
||||
collectionService = Substitute.for<CollectionService>();
|
||||
platformUtilsService = Substitute.for<PlatformUtilsService>();
|
||||
cryptoService = Substitute.for<CryptoService>();
|
||||
|
||||
importService = new ImportService(
|
||||
@@ -37,7 +34,6 @@ describe("ImportService", () => {
|
||||
apiService,
|
||||
i18nService,
|
||||
collectionService,
|
||||
platformUtilsService,
|
||||
cryptoService
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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>;
|
||||
|
||||
41
libs/common/src/emailForwarders/anonAddyForwarder.ts
Normal file
41
libs/common/src/emailForwarders/anonAddyForwarder.ts
Normal 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.";
|
||||
}
|
||||
}
|
||||
33
libs/common/src/emailForwarders/duckDuckGoForwarder.ts
Normal file
33
libs/common/src/emailForwarders/duckDuckGoForwarder.ts
Normal 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.";
|
||||
}
|
||||
}
|
||||
96
libs/common/src/emailForwarders/fastmailForwarder.ts
Normal file
96
libs/common/src/emailForwarders/fastmailForwarder.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
38
libs/common/src/emailForwarders/firefoxRelayForwarder.ts
Normal file
38
libs/common/src/emailForwarders/firefoxRelayForwarder.ts
Normal 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.";
|
||||
}
|
||||
}
|
||||
7
libs/common/src/emailForwarders/forwarder.ts
Normal file
7
libs/common/src/emailForwarders/forwarder.ts
Normal 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>;
|
||||
}
|
||||
14
libs/common/src/emailForwarders/forwarderOptions.ts
Normal file
14
libs/common/src/emailForwarders/forwarderOptions.ts
Normal 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;
|
||||
}
|
||||
48
libs/common/src/emailForwarders/simpleLoginForwarder.ts
Normal file
48
libs/common/src/emailForwarders/simpleLoginForwarder.ts
Normal 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.";
|
||||
}
|
||||
}
|
||||
4
libs/common/src/enums/encryptedExportType.ts
Normal file
4
libs/common/src/enums/encryptedExportType.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum EncryptedExportType {
|
||||
AccountEncrypted = 0,
|
||||
FileEncrypted = 1,
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
99
libs/common/src/misc/flags.spec.ts
Normal file
99
libs/common/src/misc/flags.spec.ts
Normal 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");
|
||||
});
|
||||
});
|
||||
61
libs/common/src/misc/flags.ts
Normal file
61
libs/common/src/misc/flags.ts
Normal 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];
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -27,6 +27,7 @@ export class I18nService implements I18nServiceAbstraction {
|
||||
["eo", "Esperanto"],
|
||||
["es", "español"],
|
||||
["et", "eesti"],
|
||||
["eu", "euskara"],
|
||||
["fa", "فارسی"],
|
||||
["fi", "suomi"],
|
||||
["fil", "Wikang Filipino"],
|
||||
|
||||
@@ -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
|
||||
) {}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
@@ -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.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user