mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-14 10:26:19 +01:00
Merge branch 'main' into auth/pm-14369/hide-account-switcher-if-on-login-page-and-not-logged-in
This commit is contained in:
commit
9c824628aa
4
.github/renovate.json
vendored
4
.github/renovate.json
vendored
@ -73,7 +73,7 @@
|
||||
"reviewers": ["team:team-admin-console-dev"]
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["@types/node-ipc", "node-ipc", "qrious", "regedit"],
|
||||
"matchPackageNames": ["@types/node-ipc", "node-ipc", "qrious"],
|
||||
"description": "Auth owned dependencies",
|
||||
"commitMessagePrefix": "[deps] Auth:",
|
||||
"reviewers": ["team:team-auth-dev"]
|
||||
@ -258,5 +258,5 @@
|
||||
"reviewers": ["team:team-vault-dev"]
|
||||
}
|
||||
],
|
||||
"ignoreDeps": ["@types/koa-bodyparser", "bootstrap", "node-ipc", "node", "npm", "regedit"]
|
||||
"ignoreDeps": ["@types/koa-bodyparser", "bootstrap", "node-ipc", "node", "npm"]
|
||||
}
|
||||
|
@ -128,10 +128,10 @@
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "test-storybook:build:production"
|
||||
"buildTarget": "test-storybook:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "test-storybook:build:development"
|
||||
"buildTarget": "test-storybook:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bitwarden/browser",
|
||||
"version": "2024.10.1",
|
||||
"version": "2024.11.0",
|
||||
"scripts": {
|
||||
"build": "cross-env MANIFEST_VERSION=3 webpack",
|
||||
"build:mv2": "webpack",
|
||||
|
@ -32,6 +32,7 @@ import {
|
||||
} from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||
import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -106,6 +107,7 @@ describe("OverlayBackground", () => {
|
||||
let selectedThemeMock$: BehaviorSubject<ThemeType>;
|
||||
let inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
|
||||
let themeStateService: MockProxy<ThemeStateService>;
|
||||
let totpService: MockProxy<TotpService>;
|
||||
let overlayBackground: OverlayBackground;
|
||||
let portKeyForTabSpy: Record<number, string>;
|
||||
let pageDetailsForTabSpy: PageDetailsForTab;
|
||||
@ -184,6 +186,7 @@ describe("OverlayBackground", () => {
|
||||
inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
|
||||
themeStateService = mock<ThemeStateService>();
|
||||
themeStateService.selectedTheme$ = selectedThemeMock$;
|
||||
totpService = mock<TotpService>();
|
||||
overlayBackground = new OverlayBackground(
|
||||
logService,
|
||||
cipherService,
|
||||
@ -198,6 +201,7 @@ describe("OverlayBackground", () => {
|
||||
fido2ActiveRequestManager,
|
||||
inlineMenuFieldQualificationService,
|
||||
themeStateService,
|
||||
totpService,
|
||||
generatedPasswordCallbackMock,
|
||||
addPasswordCallbackMock,
|
||||
);
|
||||
|
@ -33,6 +33,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { buildCipherIcon } from "@bitwarden/common/vault/icon/build-cipher-icon";
|
||||
@ -217,6 +218,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
private fido2ActiveRequestManager: Fido2ActiveRequestManager,
|
||||
private inlineMenuFieldQualificationService: InlineMenuFieldQualificationService,
|
||||
private themeStateService: ThemeStateService,
|
||||
private totpService: TotpService,
|
||||
private generatePasswordCallback: () => Promise<string>,
|
||||
private addPasswordCallback: (password: string) => Promise<void>,
|
||||
) {
|
||||
@ -1058,7 +1060,6 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
}
|
||||
|
||||
const cipher = this.inlineMenuCiphers.get(inlineMenuCipherId);
|
||||
|
||||
if (usePasskey && cipher.login?.hasFido2Credentials) {
|
||||
await this.authenticatePasskeyCredential(
|
||||
sender,
|
||||
@ -1066,6 +1067,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
);
|
||||
this.updateLastUsedInlineMenuCipher(inlineMenuCipherId, cipher);
|
||||
|
||||
if (cipher.login?.totp) {
|
||||
this.platformUtilsService.copyToClipboard(
|
||||
await this.totpService.getCode(cipher.login.totp),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1678,6 +1678,7 @@ export default class MainBackground {
|
||||
this.fido2ActiveRequestManager,
|
||||
inlineMenuFieldQualificationService,
|
||||
this.themeStateService,
|
||||
this.totpService,
|
||||
() => this.generatePassword(),
|
||||
(password) => this.addPasswordToHistory(password),
|
||||
);
|
||||
|
@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.10.1",
|
||||
"version": "2024.11.0",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
@ -3,7 +3,7 @@
|
||||
"minimum_chrome_version": "102.0",
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.10.1",
|
||||
"version": "2024.11.0",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@bitwarden/cli",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "2024.10.0",
|
||||
"version": "2024.11.0",
|
||||
"keywords": [
|
||||
"bitwarden",
|
||||
"password",
|
||||
@ -80,7 +80,7 @@
|
||||
"papaparse": "5.4.1",
|
||||
"proper-lockfile": "4.1.2",
|
||||
"rxjs": "7.8.1",
|
||||
"tldts": "6.1.56",
|
||||
"tldts": "6.1.58",
|
||||
"zxcvbn": "4.4.2"
|
||||
}
|
||||
}
|
||||
|
32
apps/desktop/desktop_native/Cargo.lock
generated
32
apps/desktop/desktop_native/Cargo.lock
generated
@ -546,6 +546,7 @@ dependencies = [
|
||||
"napi-derive",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2226,7 +2227,7 @@ checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-result 0.1.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
@ -2252,6 +2253,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bafa604f2104cf5ae2cc2db1dee84b7e6a5d11b05f737b60def0ffdc398cbc0a"
|
||||
dependencies = [
|
||||
"windows-result 0.2.0",
|
||||
"windows-strings",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
@ -2261,6 +2273,24 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978d65aedf914c664c510d9de43c8fd85ca745eaff1ed53edf409b479e441663"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
@ -21,5 +21,8 @@ napi-derive = "=2.16.12"
|
||||
tokio = { version = "1.38.0" }
|
||||
tokio-util = "0.7.11"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-registry = "=0.3.0"
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "=2.1.3"
|
||||
|
4
apps/desktop/desktop_native/napi/index.d.ts
vendored
4
apps/desktop/desktop_native/napi/index.d.ts
vendored
@ -51,6 +51,10 @@ export declare namespace powermonitors {
|
||||
export function onLock(callback: (err: Error | null, ) => any): Promise<void>
|
||||
export function isLockMonitorAvailable(): Promise<boolean>
|
||||
}
|
||||
export declare namespace windows_registry {
|
||||
export function createKey(key: string, subkey: string, value: string): Promise<void>
|
||||
export function deleteKey(key: string, subkey: string): Promise<void>
|
||||
}
|
||||
export declare namespace ipc {
|
||||
export interface IpcMessage {
|
||||
clientId: number
|
||||
|
@ -1,5 +1,8 @@
|
||||
#[macro_use]
|
||||
extern crate napi_derive;
|
||||
|
||||
mod registry;
|
||||
|
||||
#[napi]
|
||||
pub mod passwords {
|
||||
/// Fetch the stored password from the keychain.
|
||||
@ -190,6 +193,21 @@ pub mod powermonitors {
|
||||
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub mod windows_registry {
|
||||
#[napi]
|
||||
pub async fn create_key(key: String, subkey: String, value: String) -> napi::Result<()> {
|
||||
crate::registry::create_key(&key, &subkey, &value)
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub async fn delete_key(key: String, subkey: String) -> napi::Result<()> {
|
||||
crate::registry::delete_key(&key, &subkey)
|
||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub mod ipc {
|
||||
use desktop_core::ipc::server::{Message, MessageType};
|
||||
|
9
apps/desktop/desktop_native/napi/src/registry/dummy.rs
Normal file
9
apps/desktop/desktop_native/napi/src/registry/dummy.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
pub fn create_key(_key: &str, _subkey: &str, _value: &str) -> Result<()> {
|
||||
bail!("Not implemented")
|
||||
}
|
||||
|
||||
pub fn delete_key(_key: &str, _subkey: &str) -> Result<()> {
|
||||
bail!("Not implemented")
|
||||
}
|
4
apps/desktop/desktop_native/napi/src/registry/mod.rs
Normal file
4
apps/desktop/desktop_native/napi/src/registry/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
#[cfg_attr(target_os = "windows", path = "windows.rs")]
|
||||
#[cfg_attr(not(target_os = "windows"), path = "dummy.rs")]
|
||||
mod internal;
|
||||
pub use internal::*;
|
29
apps/desktop/desktop_native/napi/src/registry/windows.rs
Normal file
29
apps/desktop/desktop_native/napi/src/registry/windows.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
fn convert_key(key: &str) -> Result<&'static windows_registry::Key> {
|
||||
Ok(match key.to_uppercase().as_str() {
|
||||
"HKEY_CURRENT_USER" | "HKCU" => windows_registry::CURRENT_USER,
|
||||
"HKEY_LOCAL_MACHINE" | "HKLM" => windows_registry::LOCAL_MACHINE,
|
||||
"HKEY_CLASSES_ROOT" | "HKCR" => windows_registry::CLASSES_ROOT,
|
||||
_ => bail!("Invalid key"),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_key(key: &str, subkey: &str, value: &str) -> Result<()> {
|
||||
let key = convert_key(key)?;
|
||||
|
||||
let subkey = key.create(subkey)?;
|
||||
|
||||
const DEFAULT: &str = "";
|
||||
subkey.set_string(DEFAULT, value)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_key(key: &str, subkey: &str) -> Result<()> {
|
||||
let key = convert_key(key)?;
|
||||
|
||||
key.remove_tree(subkey)?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -90,13 +90,6 @@
|
||||
"electronUpdaterCompatibility": ">=0.0.1",
|
||||
"target": ["portable", "nsis-web", "appx"],
|
||||
"sign": "./sign.js",
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "../../node_modules/regedit/vbs",
|
||||
"to": "regedit/vbs",
|
||||
"filter": ["**/*"]
|
||||
}
|
||||
],
|
||||
"extraFiles": [
|
||||
{
|
||||
"from": "desktop_native/dist/desktop_proxy.${platform}-${arch}.exe",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@bitwarden/desktop",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "2024.10.3",
|
||||
"version": "2024.11.0",
|
||||
"keywords": [
|
||||
"bitwarden",
|
||||
"password",
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { existsSync, promises as fs } from "fs";
|
||||
import { homedir, userInfo } from "os";
|
||||
import * as path from "path";
|
||||
import * as util from "util";
|
||||
|
||||
import { ipcMain } from "electron";
|
||||
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { ipc } from "@bitwarden/desktop-napi";
|
||||
import { ipc, windows_registry } from "@bitwarden/desktop-napi";
|
||||
|
||||
import { isDev } from "../utils";
|
||||
|
||||
@ -142,12 +141,12 @@ export class NativeMessagingMain {
|
||||
await this.writeManifest(path.join(destination, "chrome.json"), chromeJson);
|
||||
|
||||
const nmhs = this.getWindowsNMHS();
|
||||
for (const [key, value] of Object.entries(nmhs)) {
|
||||
for (const [name, [key, subkey]] of Object.entries(nmhs)) {
|
||||
let manifestPath = path.join(destination, "chrome.json");
|
||||
if (key === "Firefox") {
|
||||
if (name === "Firefox") {
|
||||
manifestPath = path.join(destination, "firefox.json");
|
||||
}
|
||||
await this.createWindowsRegistry(value, manifestPath);
|
||||
await windows_registry.createKey(key, subkey, manifestPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -225,8 +224,8 @@ export class NativeMessagingMain {
|
||||
await this.removeIfExists(path.join(this.userPath, "browsers", "chrome.json"));
|
||||
|
||||
const nmhs = this.getWindowsNMHS();
|
||||
for (const [, value] of Object.entries(nmhs)) {
|
||||
await this.deleteWindowsRegistry(value);
|
||||
for (const [, [key, subkey]] of Object.entries(nmhs)) {
|
||||
await windows_registry.deleteKey(key, subkey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -274,11 +273,14 @@ export class NativeMessagingMain {
|
||||
|
||||
private getWindowsNMHS() {
|
||||
return {
|
||||
Firefox: "HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
Chrome: "HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
Chromium: "HKCU\\SOFTWARE\\Chromium\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
Firefox: ["HKCU", "SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden"],
|
||||
Chrome: ["HKCU", "SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden"],
|
||||
Chromium: ["HKCU", "SOFTWARE\\Chromium\\NativeMessagingHosts\\com.8bit.bitwarden"],
|
||||
// Edge uses the same registry key as Chrome as a fallback, but it's has its own separate key as well.
|
||||
"Microsoft Edge": "HKCU\\SOFTWARE\\Microsoft\\Edge\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
"Microsoft Edge": [
|
||||
"HKCU",
|
||||
"SOFTWARE\\Microsoft\\Edge\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@ -419,52 +421,6 @@ export class NativeMessagingMain {
|
||||
return path.join(path.dirname(this.exePath), `desktop_proxy${ext}`);
|
||||
}
|
||||
|
||||
private getRegeditInstance() {
|
||||
// eslint-disable-next-line
|
||||
const regedit = require("regedit");
|
||||
regedit.setExternalVBSLocation(path.join(path.dirname(this.exePath), "resources/regedit/vbs"));
|
||||
|
||||
return regedit;
|
||||
}
|
||||
|
||||
private async createWindowsRegistry(location: string, jsonFile: string) {
|
||||
const regedit = this.getRegeditInstance();
|
||||
|
||||
const createKey = util.promisify(regedit.createKey);
|
||||
const putValue = util.promisify(regedit.putValue);
|
||||
|
||||
this.logService.debug(`Adding registry: ${location}`);
|
||||
|
||||
await createKey(location);
|
||||
|
||||
// Insert path to manifest
|
||||
const obj: any = {};
|
||||
obj[location] = {
|
||||
default: {
|
||||
value: jsonFile,
|
||||
type: "REG_DEFAULT",
|
||||
},
|
||||
};
|
||||
|
||||
return putValue(obj);
|
||||
}
|
||||
|
||||
private async deleteWindowsRegistry(key: string) {
|
||||
const regedit = this.getRegeditInstance();
|
||||
|
||||
const list = util.promisify(regedit.list);
|
||||
const deleteKey = util.promisify(regedit.deleteKey);
|
||||
|
||||
this.logService.debug(`Removing registry: ${key}`);
|
||||
|
||||
try {
|
||||
await list(key);
|
||||
await deleteKey(key);
|
||||
} catch {
|
||||
this.logService.error(`Unable to delete registry key: ${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
private homedir() {
|
||||
if (process.platform === "darwin") {
|
||||
return userInfo().homedir;
|
||||
|
4
apps/desktop/src/package-lock.json
generated
4
apps/desktop/src/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@bitwarden/desktop",
|
||||
"version": "2024.10.3",
|
||||
"version": "2024.11.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@bitwarden/desktop",
|
||||
"version": "2024.10.3",
|
||||
"version": "2024.11.0",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@bitwarden/desktop-napi": "file:../desktop_native/napi",
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "@bitwarden/desktop",
|
||||
"productName": "Bitwarden",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "2024.10.3",
|
||||
"version": "2024.11.0",
|
||||
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
||||
"homepage": "https://bitwarden.com",
|
||||
"license": "GPL-3.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bitwarden/web-vault",
|
||||
"version": "2024.10.5",
|
||||
"version": "2024.11.0",
|
||||
"scripts": {
|
||||
"build:oss": "webpack",
|
||||
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",
|
||||
|
@ -23,15 +23,14 @@
|
||||
<bit-tab [label]="'role' | i18n">
|
||||
<ng-container *ngIf="!editMode">
|
||||
<p bitTypography="body1">{{ "inviteUserDesc" | i18n }}</p>
|
||||
<bit-form-field *ngIf="remainingSeats$ | async as remainingSeats">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "email" | i18n }}</bit-label>
|
||||
<input id="emails" type="text" appAutoFocus bitInput formControlName="emails" />
|
||||
<bit-hint *ngIf="remainingSeats > 1; else singleSeat">{{
|
||||
"inviteMultipleEmailDesc" | i18n: remainingSeats
|
||||
<bit-hint>{{
|
||||
"inviteMultipleEmailDesc"
|
||||
| i18n
|
||||
: (organization.productTierType === ProductTierType.TeamsStarter ? "10" : "20")
|
||||
}}</bit-hint>
|
||||
<ng-template #singleSeat>
|
||||
<bit-hint>{{ "inviteSingleEmailDesc" | i18n: remainingSeats }}</bit-hint>
|
||||
</ng-template>
|
||||
</bit-form-field>
|
||||
</ng-container>
|
||||
<bit-radio-group formControlName="type">
|
||||
@ -265,6 +264,16 @@
|
||||
<button
|
||||
*ngIf="editMode"
|
||||
type="button"
|
||||
bitIconButton="bwi-close"
|
||||
buttonType="danger"
|
||||
bitFormButton
|
||||
[appA11yTitle]="'remove' | i18n"
|
||||
[bitAction]="remove"
|
||||
[disabled]="loading"
|
||||
></button>
|
||||
<button
|
||||
*ngIf="editMode && params.managedByOrganization === true"
|
||||
type="button"
|
||||
bitIconButton="bwi-trash"
|
||||
buttonType="danger"
|
||||
bitFormButton
|
||||
|
@ -65,6 +65,7 @@ export interface MemberDialogParams {
|
||||
isOnSecretsManagerStandalone: boolean;
|
||||
initialTab?: MemberDialogTab;
|
||||
numConfirmedMembers: number;
|
||||
managedByOrganization?: boolean;
|
||||
}
|
||||
|
||||
export enum MemberDialogResult {
|
||||
@ -89,7 +90,6 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
PermissionMode = PermissionMode;
|
||||
showNoMasterPasswordWarning = false;
|
||||
isOnSecretsManagerStandalone: boolean;
|
||||
remainingSeats$: Observable<number>;
|
||||
|
||||
protected organization$: Observable<Organization>;
|
||||
protected collectionAccessItems: AccessItemView[] = [];
|
||||
@ -251,10 +251,6 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
|
||||
this.loading = false;
|
||||
});
|
||||
|
||||
this.remainingSeats$ = this.organization$.pipe(
|
||||
map((organization) => organization.seats - this.params.numConfirmedMembers),
|
||||
);
|
||||
}
|
||||
|
||||
private setFormValidators(organization: Organization) {
|
||||
@ -469,7 +465,7 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
this.close(MemberDialogResult.Saved);
|
||||
};
|
||||
|
||||
delete = async () => {
|
||||
remove = async () => {
|
||||
if (!this.editMode) {
|
||||
return;
|
||||
}
|
||||
@ -566,6 +562,39 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
this.close(MemberDialogResult.Restored);
|
||||
};
|
||||
|
||||
delete = async () => {
|
||||
if (!this.editMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: {
|
||||
key: "deleteOrganizationUser",
|
||||
placeholders: [this.params.name],
|
||||
},
|
||||
content: { key: "deleteOrganizationUserWarning" },
|
||||
type: "warning",
|
||||
acceptButtonText: { key: "delete" },
|
||||
cancelButtonText: { key: "cancel" },
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.organizationUserApiService.deleteOrganizationUser(
|
||||
this.params.organizationId,
|
||||
this.params.organizationUserId,
|
||||
);
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("organizationUserDeleted", this.params.name),
|
||||
});
|
||||
this.close(MemberDialogResult.Deleted);
|
||||
};
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
|
@ -320,6 +320,17 @@
|
||||
<i aria-hidden="true" class="bwi bwi-close"></i> {{ "remove" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="u.managedByOrganization === true"
|
||||
type="button"
|
||||
bitMenuItem
|
||||
(click)="deleteUser(u)"
|
||||
>
|
||||
<span class="tw-text-danger">
|
||||
<i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i>
|
||||
{{ "delete" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
</bit-menu>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -518,6 +518,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
isOnSecretsManagerStandalone: this.orgIsOnSecretsManagerStandalone,
|
||||
initialTab: initialTab,
|
||||
numConfirmedMembers: this.dataSource.confirmedUserCount,
|
||||
managedByOrganization: user?.managedByOrganization,
|
||||
},
|
||||
});
|
||||
|
||||
@ -725,6 +726,40 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
return true;
|
||||
}
|
||||
|
||||
async deleteUser(user: OrganizationUserView) {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: {
|
||||
key: "deleteOrganizationUser",
|
||||
placeholders: [this.userNamePipe.transform(user)],
|
||||
},
|
||||
content: { key: "deleteOrganizationUserWarning" },
|
||||
type: "warning",
|
||||
acceptButtonText: { key: "delete" },
|
||||
cancelButtonText: { key: "cancel" },
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.actionPromise = this.organizationUserApiService.deleteOrganizationUser(
|
||||
this.organization.id,
|
||||
user.id,
|
||||
);
|
||||
try {
|
||||
await this.actionPromise;
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("organizationUserDeleted", this.userNamePipe.transform(user)),
|
||||
});
|
||||
this.dataSource.removeUser(user);
|
||||
} catch (e) {
|
||||
this.validationService.showError(e);
|
||||
}
|
||||
this.actionPromise = null;
|
||||
}
|
||||
|
||||
private async noMasterPasswordConfirmationDialog(user: OrganizationUserView) {
|
||||
return this.dialogService.openSimpleDialog({
|
||||
title: {
|
||||
|
@ -21,7 +21,13 @@
|
||||
>
|
||||
{{ "purgeVault" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitButton buttonType="danger" [bitAction]="deleteAccount">
|
||||
<button
|
||||
*ngIf="showDeleteAccount$ | async"
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="danger"
|
||||
[bitAction]="deleteAccount"
|
||||
>
|
||||
{{ "deleteAccount" | i18n }}
|
||||
</button>
|
||||
</app-danger-zone>
|
||||
|
@ -23,6 +23,7 @@ export class AccountComponent implements OnInit {
|
||||
|
||||
showChangeEmail$: Observable<boolean>;
|
||||
showPurgeVault$: Observable<boolean>;
|
||||
showDeleteAccount$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
private modalService: ModalService,
|
||||
@ -63,6 +64,16 @@ export class AccountComponent implements OnInit {
|
||||
!isAccountDeprovisioningEnabled || !userIsManagedByOrganization,
|
||||
),
|
||||
);
|
||||
|
||||
this.showDeleteAccount$ = combineLatest([
|
||||
isAccountDeprovisioningEnabled$,
|
||||
userIsManagedByOrganization$,
|
||||
]).pipe(
|
||||
map(
|
||||
([isAccountDeprovisioningEnabled, userIsManagedByOrganization]) =>
|
||||
!isAccountDeprovisioningEnabled || !userIsManagedByOrganization,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async deauthorizeSessions() {
|
||||
|
@ -3218,9 +3218,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"inviteSingleEmailDesc": {
|
||||
"message": "You have 1 invite remaining."
|
||||
},
|
||||
"userUsingTwoStep": {
|
||||
"message": "This user is using two-step login to protect their account."
|
||||
},
|
||||
@ -9557,5 +9554,31 @@
|
||||
},
|
||||
"single-org-revoked-user-warning": {
|
||||
"message": "Non-compliant members will be revoked. Administrators can restore members once they leave all other organizations."
|
||||
},
|
||||
"deleteOrganizationUser": {
|
||||
"message": "Delete $NAME$",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"content": "$1",
|
||||
"example": "John Doe"
|
||||
},
|
||||
"description": "Title for the delete organization user dialog"
|
||||
}
|
||||
},
|
||||
"deleteOrganizationUserWarning": {
|
||||
"message": "When a member is deleted, their Bitwarden account and individual vault data will be permanently deleted. Collection data will remain in the organization. To reinstate them they must create an account and be onboarded again.",
|
||||
"description": "Warning for the delete organization user dialog"
|
||||
},
|
||||
"organizationUserDeleted": {
|
||||
"message": "Deleted $NAME$",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"content": "$1",
|
||||
"example": "John Doe"
|
||||
}
|
||||
}
|
||||
},
|
||||
"organizationUserDeletedDesc": {
|
||||
"message": "The user was removed from the organization and all associated user data has been deleted."
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import "core-js/stable";
|
||||
require("zone.js/dist/zone");
|
||||
import "zone.js";
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
// Production
|
||||
} else {
|
||||
// Development and test
|
||||
Error["stackTraceLimit"] = Infinity;
|
||||
require("zone.js/dist/long-stack-trace-zone");
|
||||
}
|
||||
|
@ -275,4 +275,11 @@ export abstract class OrganizationUserApiService {
|
||||
organizationId: string,
|
||||
ids: string[],
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
/**
|
||||
* Remove an organization user's access to the organization and delete their account data
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
*/
|
||||
abstract deleteOrganizationUser(organizationId: string, id: string): Promise<void>;
|
||||
}
|
||||
|
@ -359,4 +359,14 @@ export class DefaultOrganizationUserApiService implements OrganizationUserApiSer
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
deleteOrganizationUser(organizationId: string, id: string): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"DELETE",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/delete-account",
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +220,8 @@ describe("DefaultActiveUserState", () => {
|
||||
it("should not emit a previous users value if that user is no longer active", async () => {
|
||||
const user1Data: Jsonify<TestState> = {
|
||||
date: "2020-09-21T13:14:17.648Z",
|
||||
array: ["value"],
|
||||
// NOTE: `as any` is here until we migrate to Nx: https://bitwarden.atlassian.net/browse/PM-6493
|
||||
array: ["value"] as any,
|
||||
};
|
||||
const user2Data: Jsonify<TestState> = {
|
||||
date: "2020-09-21T13:14:17.648Z",
|
||||
|
@ -192,7 +192,8 @@ describe("KeyDefinition", () => {
|
||||
expect(arrayDefinition).toBeTruthy();
|
||||
expect(arrayDefinition.deserializer).toBeTruthy();
|
||||
|
||||
const deserializedValue = arrayDefinition.deserializer([false, true]);
|
||||
// NOTE: `as any` is here until we migrate to Nx: https://bitwarden.atlassian.net/browse/PM-6493
|
||||
const deserializedValue = arrayDefinition.deserializer([false, true] as any);
|
||||
|
||||
expect(deserializedValue).toBeTruthy();
|
||||
expect(deserializedValue).toHaveLength(2);
|
||||
|
@ -151,6 +151,7 @@ describe("Login DTO", () => {
|
||||
password: "myPassword" as EncryptedString,
|
||||
passwordRevisionDate: passwordRevisionDate.toISOString(),
|
||||
totp: "myTotp" as EncryptedString,
|
||||
// NOTE: `as any` is here until we migrate to Nx: https://bitwarden.atlassian.net/browse/PM-6493
|
||||
fido2Credentials: [
|
||||
{
|
||||
credentialId: "keyId" as EncryptedString,
|
||||
@ -167,7 +168,7 @@ describe("Login DTO", () => {
|
||||
discoverable: "discoverable" as EncryptedString,
|
||||
creationDate: fido2CreationDate.toISOString(),
|
||||
},
|
||||
],
|
||||
] as any,
|
||||
});
|
||||
|
||||
expect(actual).toEqual({
|
||||
|
@ -71,7 +71,7 @@ The content can be a button, anchor, or static container.
|
||||
<bit-item>
|
||||
<button bit-item-content type="button">
|
||||
<bit-avatar slot="start" text="Foo"></bit-avatar>
|
||||
foo@bitwarden.com
|
||||
foo@bitwarden.com
|
||||
<span bitBadge variant="primary" slot="default-trailing">Auto-fill</span>
|
||||
<ng-container slot="secondary">
|
||||
<div>Bitwarden.com</div>
|
||||
|
@ -94,7 +94,7 @@ export const ContentSlots: Story = {
|
||||
slot="start"
|
||||
[text]="'Foo'"
|
||||
></bit-avatar>
|
||||
foo@bitwarden.com
|
||||
foo@bitwarden.com
|
||||
<ng-container slot="secondary">
|
||||
<div>Bitwarden.com</div>
|
||||
<div><em>locked</em></div>
|
||||
@ -285,37 +285,37 @@ export const SingleActionList: Story = {
|
||||
<a bit-item-content href="#">
|
||||
Foobar
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<a bit-item-content href="#">
|
||||
Foobar
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<a bit-item-content href="#">
|
||||
Foobar
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<a bit-item-content href="#">
|
||||
Foobar
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<a bit-item-content href="#">
|
||||
Foobar
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<a bit-item-content href="#">
|
||||
Foobar
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</a>
|
||||
</bit-item>
|
||||
</bit-item-group>
|
||||
`,
|
||||
@ -332,14 +332,14 @@ export const SingleActionWithBadge: Story = {
|
||||
Foobar
|
||||
<span bitBadge variant="primary" slot="default-trailing">Auto-fill</span>
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<a bit-item-content href="#">
|
||||
Helloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo!
|
||||
<span bitBadge variant="primary" slot="default-trailing">Auto-fill</span>
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</a>
|
||||
</bit-item>
|
||||
</bit-item-group>
|
||||
`,
|
||||
|
11511
package-lock.json
generated
11511
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
50
package.json
50
package.json
@ -35,19 +35,20 @@
|
||||
"libs/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "16.2.14",
|
||||
"@angular-eslint/eslint-plugin": "16.3.1",
|
||||
"@angular-eslint/eslint-plugin-template": "16.3.1",
|
||||
"@angular-eslint/template-parser": "16.3.1",
|
||||
"@angular/cli": "16.2.14",
|
||||
"@angular/compiler-cli": "16.2.12",
|
||||
"@angular/elements": "16.2.12",
|
||||
"@angular-devkit/build-angular": "17.3.9",
|
||||
"@angular-eslint/eslint-plugin": "17.5.3",
|
||||
"@angular-eslint/eslint-plugin-template": "17.5.3",
|
||||
"@angular-eslint/schematics": "17.5.3",
|
||||
"@angular-eslint/template-parser": "17.5.3",
|
||||
"@angular/cli": "17.3.9",
|
||||
"@angular/compiler-cli": "17.3.12",
|
||||
"@angular/elements": "17.3.12",
|
||||
"@babel/core": "7.24.9",
|
||||
"@babel/preset-env": "7.24.8",
|
||||
"@compodoc/compodoc": "1.1.25",
|
||||
"@electron/notarize": "2.5.0",
|
||||
"@electron/rebuild": "3.7.0",
|
||||
"@ngtools/webpack": "16.2.14",
|
||||
"@ngtools/webpack": "17.3.9",
|
||||
"@storybook/addon-a11y": "8.2.9",
|
||||
"@storybook/addon-actions": "8.2.9",
|
||||
"@storybook/addon-designs": "8.0.3",
|
||||
@ -58,7 +59,7 @@
|
||||
"@storybook/manager-api": "8.2.9",
|
||||
"@storybook/theming": "8.2.9",
|
||||
"@types/argon2-browser": "1.18.4",
|
||||
"@types/chrome": "0.0.272",
|
||||
"@types/chrome": "0.0.280",
|
||||
"@types/firefox-webext-browser": "120.0.4",
|
||||
"@types/inquirer": "8.2.10",
|
||||
"@types/jest": "29.5.12",
|
||||
@ -127,7 +128,6 @@
|
||||
"prettier": "3.3.3",
|
||||
"prettier-plugin-tailwindcss": "0.6.8",
|
||||
"process": "0.11.10",
|
||||
"regedit": "3.0.3",
|
||||
"remark-gfm": "4.0.0",
|
||||
"rimraf": "6.0.1",
|
||||
"sass": "1.74.1",
|
||||
@ -139,7 +139,7 @@
|
||||
"ts-loader": "9.5.1",
|
||||
"tsconfig-paths-webpack-plugin": "4.1.0",
|
||||
"type-fest": "2.19.0",
|
||||
"typescript": "5.1.6",
|
||||
"typescript": "5.4.2",
|
||||
"url": "0.11.4",
|
||||
"util": "0.12.5",
|
||||
"wait-on": "8.0.1",
|
||||
@ -149,22 +149,22 @@
|
||||
"webpack-node-externals": "3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "16.2.12",
|
||||
"@angular/cdk": "16.2.14",
|
||||
"@angular/common": "16.2.12",
|
||||
"@angular/compiler": "16.2.12",
|
||||
"@angular/core": "16.2.12",
|
||||
"@angular/forms": "16.2.12",
|
||||
"@angular/platform-browser": "16.2.12",
|
||||
"@angular/platform-browser-dynamic": "16.2.12",
|
||||
"@angular/router": "16.2.12",
|
||||
"@angular/animations": "17.3.12",
|
||||
"@angular/cdk": "17.3.10",
|
||||
"@angular/common": "17.3.12",
|
||||
"@angular/compiler": "17.3.12",
|
||||
"@angular/core": "17.3.12",
|
||||
"@angular/forms": "17.3.12",
|
||||
"@angular/platform-browser": "17.3.12",
|
||||
"@angular/platform-browser-dynamic": "17.3.12",
|
||||
"@angular/router": "17.3.12",
|
||||
"@bitwarden/sdk-internal": "0.1.7",
|
||||
"@electron/fuses": "1.8.0",
|
||||
"@koa/multer": "3.0.2",
|
||||
"@koa/router": "13.1.0",
|
||||
"@microsoft/signalr": "8.0.7",
|
||||
"@microsoft/signalr-protocol-msgpack": "8.0.7",
|
||||
"@ng-select/ng-select": "11.2.0",
|
||||
"@ng-select/ng-select": "12.0.7",
|
||||
"argon2": "0.41.1",
|
||||
"argon2-browser": "1.18.0",
|
||||
"big-integer": "1.6.52",
|
||||
@ -187,8 +187,8 @@
|
||||
"lowdb": "1.0.0",
|
||||
"lunr": "2.3.9",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"ngx-infinite-scroll": "16.0.0",
|
||||
"ngx-toastr": "17.0.2",
|
||||
"ngx-infinite-scroll": "17.0.1",
|
||||
"ngx-toastr": "18.0.0",
|
||||
"node-fetch": "2.6.12",
|
||||
"node-forge": "1.3.1",
|
||||
"nord": "0.2.1",
|
||||
@ -202,9 +202,9 @@
|
||||
"qrious": "4.0.2",
|
||||
"rxjs": "7.8.1",
|
||||
"tabbable": "6.2.0",
|
||||
"tldts": "6.1.56",
|
||||
"tldts": "6.1.58",
|
||||
"utf-8-validate": "6.0.4",
|
||||
"zone.js": "0.13.3",
|
||||
"zone.js": "0.14.10",
|
||||
"zxcvbn": "4.4.2"
|
||||
},
|
||||
"overrides": {
|
||||
|
Loading…
Reference in New Issue
Block a user