1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-22 21:21:35 +01:00

Merge branch 'main' into autofill/pm-5189-fix-issues-present-with-inline-menu-rendering-in-iframes

This commit is contained in:
Cesar Gonzalez 2024-06-24 17:40:53 -05:00 committed by GitHub
commit 6112d989ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 545 additions and 1023 deletions

View File

@ -415,6 +415,9 @@ jobs:
cache-dependency-path: '**/package-lock.json'
node-version: ${{ env._NODE_VERSION }}
- name: Set up Node-gyp
run: python3 -m pip install setuptools
- name: Print environment
run: |
node --version
@ -546,6 +549,9 @@ jobs:
cache-dependency-path: '**/package-lock.json'
node-version: ${{ env._NODE_VERSION }}
- name: Set up Node-gyp
run: python3 -m pip install setuptools
- name: Print environment
run: |
node --version
@ -756,6 +762,9 @@ jobs:
cache-dependency-path: '**/package-lock.json'
node-version: ${{ env._NODE_VERSION }}
- name: Set up Node-gyp
run: python3 -m pip install setuptools
- name: Print environment
run: |
node --version

View File

@ -58,7 +58,7 @@
"dependencies": {
"@koa/multer": "3.0.2",
"@koa/router": "12.0.1",
"argon2": "0.31.0",
"argon2": "0.40.1",
"big-integer": "1.6.51",
"browser-hrtime": "1.1.8",
"chalk": "4.1.2",
@ -80,7 +80,7 @@
"papaparse": "5.4.1",
"proper-lockfile": "4.1.2",
"rxjs": "7.8.1",
"tldts": "6.1.25",
"tldts": "6.1.29",
"zxcvbn": "4.4.2"
}
}

View File

@ -20,9 +20,9 @@
"**/node_modules/@bitwarden/desktop-native/desktop_native.${platform}-${arch}*.node",
"!**/node_modules/argon2/**/*",
"**/node_modules/argon2/argon2.js",
"**/node_modules/argon2/argon2.cjs",
"**/node_modules/argon2/package.json",
"**/node_modules/argon2/lib/binding/napi-v3/argon2.node"
"**/node_modules/argon2/build/Release/argon2.node"
],
"electronVersion": "30.1.2",
"generateUpdatesFilesForAllChannels": true,

View File

@ -10,10 +10,11 @@
"license": "GPL-3.0",
"dependencies": {
"@bitwarden/desktop-native": "file:../desktop_native",
"argon2": "0.31.0"
"argon2": "0.40.1"
}
},
"../desktop_native": {
"name": "@bitwarden/desktop-native",
"version": "0.1.0",
"license": "GPL-3.0",
"devDependencies": {
@ -24,25 +25,6 @@
"resolved": "../desktop_native",
"link": true
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
"dependencies": {
"detect-libc": "^2.0.0",
"https-proxy-agent": "^5.0.0",
"make-dir": "^3.1.0",
"node-fetch": "^2.6.7",
"nopt": "^5.0.0",
"npmlog": "^5.0.1",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.11"
},
"bin": {
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@phc/format": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
@ -51,330 +33,20 @@
"node": ">=10"
}
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/aproba": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
},
"node_modules/are-we-there-yet": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/argon2": {
"version": "0.31.0",
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.31.0.tgz",
"integrity": "sha512-r56NWwlE3tjD/FIqL1T+V4Ka+Mb5yMF35w1YWHpwpEjeONXBUbxmjhWkWqY63mse8lpcZ+ZZIGpKL+s+qXhyfg==",
"version": "0.40.1",
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz",
"integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==",
"hasInstallScript": true,
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.11",
"@phc/format": "^1.0.0",
"node-addon-api": "^7.0.0"
"node-addon-api": "^7.1.0",
"node-gyp-build": "^4.8.0"
},
"engines": {
"node": ">=14.0.0"
"node": ">=16.17.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"engines": {
"node": ">=10"
}
},
"node_modules/color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"bin": {
"color-support": "bin.js"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
},
"node_modules/detect-libc": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
"engines": {
"node": ">=8"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/fs-minipass/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/gauge": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
"dependencies": {
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.2",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.1",
"object-assign": "^4.1.1",
"signal-exit": "^3.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-dir/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/minipass": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/minizlib/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/node-addon-api": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz",
@ -383,233 +55,15 @@
"node": "^16 || ^18 || >= 20"
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
"dependencies": {
"abbrev": "1"
},
"node_modules/node-gyp-build": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz",
"integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==",
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": ">=6"
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/npmlog": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
"dependencies": {
"are-we-there-yet": "^2.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^3.0.0",
"set-blocking": "^2.0.0"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/semver": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/tar": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^5.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dependencies": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
}

View File

@ -13,6 +13,6 @@
},
"dependencies": {
"@bitwarden/desktop-native": "file:../desktop_native",
"argon2": "0.31.0"
"argon2": "0.40.1"
}
}

View File

@ -32,4 +32,5 @@ export abstract class VaultFilterService {
) => Observable<TreeNode<CipherTypeFilter>>;
// TODO: Remove this from org vault when collection admin service adopts state management
reloadCollections?: (collections: CollectionAdminView[]) => void;
clearOrganizationFilter: () => void;
}

View File

@ -103,6 +103,10 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
return this._organizationFilter;
}
clearOrganizationFilter() {
this._organizationFilter.next(null);
}
setOrganizationFilter(organization: Organization) {
if (organization?.id != "AllVaults") {
this._organizationFilter.next(organization);

View File

@ -394,6 +394,7 @@ export class VaultComponent implements OnInit, OnDestroy {
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
this.destroy$.next();
this.destroy$.complete();
this.vaultFilterService.clearOrganizationFilter();
}
async onVaultItemsEvent(event: VaultItemEvent) {

View File

@ -7892,7 +7892,7 @@
"message": "Adjustments to seats will be reflected in the next billing cycle."
},
"unassignedSeatsDescription": {
"message": "Unassigned subscription seats"
"message": "Unassigned seats"
},
"purchaseSeatDescription": {
"message": "Additional seats purchased"
@ -8399,10 +8399,6 @@
"exportClientReport": {
"message": "Export client report"
},
"invoiceNumberHeader": {
"message": "Invoice number",
"description": "A table header for an invoice's number"
},
"memberAccessReport": {
"message": "Member access"
},
@ -8450,5 +8446,29 @@
},
"smAccessRemovalSecretMessage": {
"message": "This action will remove your access to this secret."
},
"invoice": {
"message": "Invoice"
},
"unassignedSeatsAvailable": {
"message": "You have $SEATS$ unassigned seats available.",
"placeholders": {
"seats": {
"content": "$1",
"example": "10"
}
},
"description": "A message showing how many unassigned seats are available for a provider."
},
"contactYourProviderForAdditionalSeats": {
"message": "Contact your provider admin to purchase additional seats."
},
"open": {
"message": "Open",
"description": "The status of an invoice."
},
"uncollectible": {
"message": "Uncollectible",
"description": "The status of an invoice."
}
}

View File

@ -10,7 +10,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { canAccessBilling } from "@bitwarden/common/billing/abstractions/provider-billing.service.abstraction";
import { hasConsolidatedBilling } from "@bitwarden/common/billing/abstractions/provider-billing.service.abstraction";
import { PlanType } from "@bitwarden/common/billing/enums";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -72,9 +72,9 @@ export class ClientsComponent extends BaseClientsComponent {
switchMap((params) => {
this.providerId = params.providerId;
return this.providerService.get$(this.providerId).pipe(
canAccessBilling(this.configService),
map((canAccessBilling) => {
if (canAccessBilling) {
hasConsolidatedBilling(this.configService),
map((hasConsolidatedBilling) => {
if (hasConsolidatedBilling) {
return from(
this.router.navigate(["../manage-client-organizations"], {
relativeTo: this.activatedRoute,

View File

@ -5,7 +5,7 @@
<bit-nav-item
icon="bwi-bank"
[text]="'clients' | i18n"
[route]="(canAccessBilling$ | async) ? 'manage-client-organizations' : 'clients'"
[route]="(hasConsolidatedBilling$ | async) ? 'manage-client-organizations' : 'clients'"
></bit-nav-item>
<bit-nav-group
icon="bwi-sliders"

View File

@ -1,13 +1,13 @@
import { CommonModule } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute, RouterModule } from "@angular/router";
import { switchMap, Observable, Subject, filter, startWith } from "rxjs";
import { switchMap, Observable, Subject, combineLatest, map } from "rxjs";
import { takeUntil } from "rxjs/operators";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
import { canAccessBilling } from "@bitwarden/common/billing/abstractions/provider-billing.service.abstraction";
import { hasConsolidatedBilling } from "@bitwarden/common/billing/abstractions/provider-billing.service.abstraction";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { IconModule, LayoutComponent, NavigationModule } from "@bitwarden/components";
@ -37,6 +37,8 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
protected provider$: Observable<Provider>;
protected hasConsolidatedBilling$: Observable<boolean>;
protected canAccessBilling$: Observable<boolean>;
protected showPaymentMethodWarningBanners$ = this.configService.getFeatureFlag$(
@ -57,10 +59,15 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy {
takeUntil(this.destroy$),
);
this.canAccessBilling$ = this.provider$.pipe(
filter((provider) => !!provider),
canAccessBilling(this.configService),
startWith(false),
this.hasConsolidatedBilling$ = this.provider$.pipe(
hasConsolidatedBilling(this.configService),
takeUntil(this.destroy$),
);
this.canAccessBilling$ = combineLatest([this.hasConsolidatedBilling$, this.provider$]).pipe(
map(
([hasConsolidatedBilling, provider]) => hasConsolidatedBilling && provider.isProviderAdmin,
),
takeUntil(this.destroy$),
);
}

View File

@ -9,7 +9,7 @@ import { FrontendLayoutComponent } from "@bitwarden/web-vault/app/layouts/fronte
import { UserLayoutComponent } from "@bitwarden/web-vault/app/layouts/user-layout.component";
import {
ManageClientOrganizationsComponent,
ManageClientsComponent,
ProviderSubscriptionComponent,
hasConsolidatedBilling,
ProviderPaymentMethodComponent,
@ -85,7 +85,7 @@ const routes: Routes = [
{
path: "manage-client-organizations",
canActivate: [hasConsolidatedBilling],
component: ManageClientOrganizationsComponent,
component: ManageClientsComponent,
data: { titleId: "clients" },
},
{
@ -118,7 +118,7 @@ const routes: Routes = [
},
{
path: "billing",
canActivate: [hasConsolidatedBilling],
canActivate: [ProviderPermissionsGuard, hasConsolidatedBilling],
data: { providerPermissions: (provider: Provider) => provider.isProviderAdmin },
children: [
{
@ -129,6 +129,7 @@ const routes: Routes = [
{
path: "subscription",
component: ProviderSubscriptionComponent,
canActivate: [ProviderPermissionsGuard],
data: {
titleId: "subscription",
},
@ -136,6 +137,7 @@ const routes: Routes = [
{
path: "payment-method",
component: ProviderPaymentMethodComponent,
canActivate: [ProviderPermissionsGuard],
data: {
titleId: "paymentMethod",
},
@ -143,6 +145,7 @@ const routes: Routes = [
{
path: "history",
component: ProviderBillingHistoryComponent,
canActivate: [ProviderPermissionsGuard],
data: {
titleId: "billingHistory",
},

View File

@ -10,11 +10,11 @@ import { PaymentMethodWarningsModule } from "@bitwarden/web-vault/app/billing/sh
import { OssModule } from "@bitwarden/web-vault/app/oss.module";
import {
CreateClientOrganizationComponent,
CreateClientDialogComponent,
NoClientsComponent,
ManageClientOrganizationNameComponent,
ManageClientOrganizationsComponent,
ManageClientOrganizationSubscriptionComponent,
ManageClientNameDialogComponent,
ManageClientsComponent,
ManageClientSubscriptionDialogComponent,
ProviderBillingHistoryComponent,
ProviderPaymentMethodComponent,
ProviderSelectPaymentMethodDialogComponent,
@ -66,11 +66,11 @@ import { SetupComponent } from "./setup/setup.component";
SetupComponent,
SetupProviderComponent,
UserAddEditComponent,
CreateClientOrganizationComponent,
CreateClientDialogComponent,
NoClientsComponent,
ManageClientOrganizationsComponent,
ManageClientOrganizationNameComponent,
ManageClientOrganizationSubscriptionComponent,
ManageClientsComponent,
ManageClientNameDialogComponent,
ManageClientSubscriptionDialogComponent,
ProviderBillingHistoryComponent,
ProviderSubscriptionComponent,
ProviderSelectPaymentMethodDialogComponent,

View File

@ -56,13 +56,15 @@
<input type="text" bitInput formControlName="seats" />
<bit-hint
class="tw-text-muted tw-grid tw-grid-flow-col tw-gap-1 tw-grid-cols-1 tw-grid-rows-2"
*ngIf="unassignedSeatsForSelectedPlan > 0"
*ngIf="openSeats > 0"
>
<span class="tw-col-span-1"
>{{ unassignedSeatsForSelectedPlan }}
{{ "unassignedSeatsDescription" | i18n | lowercase }}</span
>{{ unassignedSeats }} {{ "unassignedSeatsDescription" | i18n | lowercase }}</span
>
<span class="tw-col-span-1"
>{{ additionalSeatsPurchased }}
{{ "purchaseSeatDescription" | i18n | lowercase }}</span
>
<span class="tw-col-span-1">0 {{ "purchaseSeatDescription" | i18n | lowercase }}</span>
</bit-hint>
</bit-form-field>
</div>

View File

@ -1,6 +1,6 @@
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { PlanType } from "@bitwarden/common/billing/enums";
@ -11,22 +11,22 @@ import { DialogService, ToastService } from "@bitwarden/components";
import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service";
type CreateClientOrganizationParams = {
type CreateClientDialogParams = {
providerId: string;
plans: PlanResponse[];
};
export enum CreateClientOrganizationResultType {
export enum CreateClientDialogResultType {
Closed = "closed",
Submitted = "submitted",
}
export const openCreateClientOrganizationDialog = (
export const openCreateClientDialog = (
dialogService: DialogService,
dialogConfig: DialogConfig<CreateClientOrganizationParams>,
dialogConfig: DialogConfig<CreateClientDialogParams>,
) =>
dialogService.open<CreateClientOrganizationResultType, CreateClientOrganizationParams>(
CreateClientOrganizationComponent,
dialogService.open<CreateClientDialogResultType, CreateClientDialogParams>(
CreateClientDialogComponent,
dialogConfig,
);
@ -39,26 +39,24 @@ type PlanCard = {
};
@Component({
selector: "app-create-client-organization",
templateUrl: "./create-client-organization.component.html",
templateUrl: "./create-client-dialog.component.html",
})
export class CreateClientOrganizationComponent implements OnInit {
protected formGroup = this.formBuilder.group({
clientOwnerEmail: ["", [Validators.required, Validators.email]],
organizationName: ["", Validators.required],
seats: [null, [Validators.required, Validators.min(1)]],
export class CreateClientDialogComponent implements OnInit {
protected formGroup = new FormGroup({
clientOwnerEmail: new FormControl<string>("", [Validators.required, Validators.email]),
organizationName: new FormControl<string>("", [Validators.required]),
seats: new FormControl<number>(null, [Validators.required, Validators.min(1)]),
});
protected loading = true;
protected planCards: PlanCard[];
protected ResultType = CreateClientOrganizationResultType;
protected ResultType = CreateClientDialogResultType;
private providerPlans: ProviderPlanResponse[];
constructor(
private billingApiService: BillingApiServiceAbstraction,
@Inject(DIALOG_DATA) private dialogParams: CreateClientOrganizationParams,
private dialogRef: DialogRef<CreateClientOrganizationResultType>,
private formBuilder: FormBuilder,
@Inject(DIALOG_DATA) private dialogParams: CreateClientDialogParams,
private dialogRef: DialogRef<CreateClientDialogResultType>,
private i18nService: I18nService,
private toastService: ToastService,
private webProviderService: WebProviderService,
@ -111,14 +109,14 @@ export class CreateClientOrganizationComponent implements OnInit {
this.planCards = [
{
name: this.i18nService.t("planNameTeams"),
cost: teamsPlan.PasswordManager.seatPrice * 0.65, // 35% off for MSPs,
cost: teamsPlan.PasswordManager.providerPortalSeatPrice * 0.65, // 35% off for MSPs,
type: teamsPlan.type,
plan: teamsPlan,
selected: true,
},
{
name: this.i18nService.t("planNameEnterprise"),
cost: enterprisePlan.PasswordManager.seatPrice * 0.65, // 35% off for MSPs,
cost: enterprisePlan.PasswordManager.providerPortalSeatPrice * 0.65, // 35% off for MSPs,
type: enterprisePlan.type,
plan: enterprisePlan,
selected: false,
@ -159,14 +157,41 @@ export class CreateClientOrganizationComponent implements OnInit {
this.dialogRef.close(this.ResultType.Submitted);
};
protected get unassignedSeatsForSelectedPlan(): number {
if (this.loading || !this.planCards) {
protected get openSeats(): number {
const selectedProviderPlan = this.getSelectedProviderPlan();
if (selectedProviderPlan === null) {
return 0;
}
const selectedPlan = this.planCards.find((planCard) => planCard.selected).plan;
const selectedProviderPlan = this.providerPlans.find(
(providerPlan) => providerPlan.planName === selectedPlan.name,
);
return selectedProviderPlan.seatMinimum - selectedProviderPlan.assignedSeats;
}
protected get unassignedSeats(): number {
const unassignedSeats = this.openSeats - this.formGroup.value.seats;
return unassignedSeats > 0 ? unassignedSeats : 0;
}
protected get additionalSeatsPurchased(): number {
const selectedProviderPlan = this.getSelectedProviderPlan();
if (selectedProviderPlan === null) {
return 0;
}
const selectedSeats = this.formGroup.value.seats ?? 0;
const purchased = selectedSeats - this.openSeats;
return purchased > 0 ? purchased : 0;
}
private getSelectedProviderPlan(): ProviderPlanResponse {
if (this.loading || !this.planCards) {
return null;
}
const selectedPlan = this.planCards.find((planCard) => planCard.selected).plan;
return this.providerPlans.find((providerPlan) => providerPlan.planName === selectedPlan.name);
}
}

View File

@ -1,5 +1,5 @@
export * from "./create-client-organization.component";
export * from "./manage-client-organizations.component";
export * from "./manage-client-organization-name.component";
export * from "./manage-client-organization-subscription.component";
export * from "./create-client-dialog.component";
export * from "./manage-clients.component";
export * from "./manage-client-name-dialog.component";
export * from "./manage-client-subscription-dialog.component";
export * from "./no-clients.component";

View File

@ -7,7 +7,7 @@ import { UpdateClientOrganizationRequest } from "@bitwarden/common/billing/model
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, ToastService } from "@bitwarden/components";
type ManageClientOrganizationNameParams = {
type ManageClientNameDialogParams = {
providerId: string;
organization: {
id: string;
@ -16,34 +16,33 @@ type ManageClientOrganizationNameParams = {
};
};
export enum ManageClientOrganizationNameResultType {
export enum ManageClientNameDialogResultType {
Closed = "closed",
Submitted = "submitted",
}
export const openManageClientOrganizationNameDialog = (
export const openManageClientNameDialog = (
dialogService: DialogService,
dialogConfig: DialogConfig<ManageClientOrganizationNameParams>,
dialogConfig: DialogConfig<ManageClientNameDialogParams>,
) =>
dialogService.open<ManageClientOrganizationNameResultType, ManageClientOrganizationNameParams>(
ManageClientOrganizationNameComponent,
dialogService.open<ManageClientNameDialogResultType, ManageClientNameDialogParams>(
ManageClientNameDialogComponent,
dialogConfig,
);
@Component({
selector: "app-manage-client-organization-name",
templateUrl: "manage-client-organization-name.component.html",
templateUrl: "manage-client-name-dialog.component.html",
})
export class ManageClientOrganizationNameComponent {
protected ResultType = ManageClientOrganizationNameResultType;
export class ManageClientNameDialogComponent {
protected ResultType = ManageClientNameDialogResultType;
protected formGroup = this.formBuilder.group({
name: [this.dialogParams.organization.name, Validators.required],
});
constructor(
@Inject(DIALOG_DATA) protected dialogParams: ManageClientOrganizationNameParams,
@Inject(DIALOG_DATA) protected dialogParams: ManageClientNameDialogParams,
private billingApiService: BillingApiServiceAbstraction,
private dialogRef: DialogRef<ManageClientOrganizationNameResultType>,
private dialogRef: DialogRef<ManageClientNameDialogResultType>,
private formBuilder: FormBuilder,
private i18nService: I18nService,
private toastService: ToastService,

View File

@ -1,40 +0,0 @@
<bit-dialog dialogSize="large" [loading]="loading">
<span bitDialogTitle>
{{ "manageSeats" | i18n }}
<small class="tw-text-muted" *ngIf="clientName">{{ clientName }}</small>
</span>
<div bitDialogContent>
<p>
{{ "manageSeatsDescription" | i18n }}
</p>
<bit-form-field disableMargin>
<bit-label>
{{ "assignedSeats" | i18n }}
</bit-label>
<input id="assignedSeats" type="number" bitInput required [(ngModel)]="assignedSeats" />
<bit-hint class="tw-text-muted" *ngIf="remainingOpenSeats > 0">
<div class="tw-grid tw-grid-flow-col tw-gap-1 tw-grid-cols-1 tw-grid-rows-2">
<span class="tw-col-span-1"
>{{ unassignedSeats }} {{ "unassignedSeatsDescription" | i18n | lowercase }}</span
>
<span class="tw-col-span-1">0 {{ "purchaseSeatDescription" | i18n | lowercase }}</span>
</div>
</bit-hint>
</bit-form-field>
</div>
<ng-container bitDialogFooter>
<button
type="submit"
bitButton
buttonType="primary"
bitFormButton
(click)="updateSubscription(assignedSeats)"
>
<i class="bwi bwi-refresh bwi-fw" aria-hidden="true"></i>
{{ "save" | i18n }}
</button>
<button bitButton type="button" buttonType="secondary" bitDialogClose>
{{ "cancel" | i18n }}
</button>
</ng-container>
</bit-dialog>

View File

@ -1,116 +0,0 @@
import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core";
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { UpdateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/update-client-organization.request";
import { ProviderPlanResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService } from "@bitwarden/components";
type ManageClientOrganizationDialogParams = {
organization: ProviderOrganizationOrganizationDetailsResponse;
};
@Component({
templateUrl: "manage-client-organization-subscription.component.html",
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class ManageClientOrganizationSubscriptionComponent implements OnInit {
loading = true;
providerOrganizationId: string;
providerId: string;
clientName: string;
assignedSeats: number;
unassignedSeats: number;
planName: string;
AdditionalSeatPurchased: number;
remainingOpenSeats: number;
constructor(
public dialogRef: DialogRef,
@Inject(DIALOG_DATA) protected data: ManageClientOrganizationDialogParams,
private billingApiService: BillingApiService,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
) {
this.providerOrganizationId = data.organization.id;
this.providerId = data.organization.providerId;
this.clientName = data.organization.organizationName;
this.assignedSeats = data.organization.seats;
this.planName = data.organization.plan;
}
async ngOnInit() {
try {
const response = await this.billingApiService.getProviderSubscription(this.providerId);
this.AdditionalSeatPurchased = this.getPurchasedSeatsByPlan(this.planName, response.plans);
const seatMinimum = this.getProviderSeatMinimumByPlan(this.planName, response.plans);
const assignedByPlan = this.getAssignedByPlan(this.planName, response.plans);
this.remainingOpenSeats = seatMinimum - assignedByPlan;
this.unassignedSeats = Math.abs(this.remainingOpenSeats);
} catch (error) {
this.remainingOpenSeats = 0;
this.AdditionalSeatPurchased = 0;
}
this.loading = false;
}
async updateSubscription(assignedSeats: number) {
this.loading = true;
if (!assignedSeats) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("assignedSeatCannotUpdate"),
);
return;
}
const request = new UpdateClientOrganizationRequest();
request.assignedSeats = assignedSeats;
request.name = this.clientName;
await this.billingApiService.updateClientOrganization(
this.providerId,
this.providerOrganizationId,
request,
);
this.platformUtilsService.showToast("success", null, this.i18nService.t("subscriptionUpdated"));
this.loading = false;
this.dialogRef.close();
}
getPurchasedSeatsByPlan(planName: string, plans: ProviderPlanResponse[]): number {
const plan = plans.find((plan) => plan.planName === planName);
if (plan) {
return plan.purchasedSeats;
} else {
return 0;
}
}
getAssignedByPlan(planName: string, plans: ProviderPlanResponse[]): number {
const plan = plans.find((plan) => plan.planName === planName);
if (plan) {
return plan.assignedSeats;
} else {
return 0;
}
}
getProviderSeatMinimumByPlan(planName: string, plans: ProviderPlanResponse[]) {
const plan = plans.find((plan) => plan.planName === planName);
if (plan) {
return plan.seatMinimum;
} else {
return 0;
}
}
static open(dialogService: DialogService, data: ManageClientOrganizationDialogParams) {
return dialogService.open(ManageClientOrganizationSubscriptionComponent, { data });
}
}

View File

@ -0,0 +1,46 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-dialog dialogSize="large" [loading]="loading">
<span bitDialogTitle>
{{ "manageSeats" | i18n }}
<small class="tw-text-muted">{{ dialogParams.organization.organizationName }}</small>
</span>
<div bitDialogContent>
<p>{{ "manageSeatsDescription" | i18n }}</p>
<bit-form-field disableMargin>
<bit-label>
{{ "assignedSeats" | i18n }}
</bit-label>
<input type="number" bitInput formControlName="assignedSeats" />
<bit-hint class="tw-text-muted" *ngIf="openSeats > 0 || isServiceUserWithPurchasedSeats">
<div
*ngIf="!this.isServiceUserWithPurchasedSeats"
class="tw-grid tw-grid-flow-col tw-gap-1 tw-grid-cols-1"
[ngClass]="{ 'tw-grid-rows-2': this.isProviderAdmin }"
>
<span class="tw-col-span-1">
{{ unassignedSeats }} {{ "unassignedSeatsDescription" | i18n | lowercase }}
</span>
<span *ngIf="this.isProviderAdmin" class="tw-col-span-1"
>{{ additionalSeatsPurchased }}
{{ "purchaseSeatDescription" | i18n | lowercase }}</span
>
</div>
</bit-hint>
</bit-form-field>
</div>
<ng-container bitDialogFooter>
<button
bitButton
bitFormButton
buttonType="primary"
type="submit"
[disabled]="formGroup.invalid"
>
{{ "save" | i18n }}
</button>
<button bitButton buttonType="secondary" type="button" [bitDialogClose]="ResultType.Closed">
{{ "cancel" | i18n }}
</button>
</ng-container>
</bit-dialog>
</form>

View File

@ -0,0 +1,180 @@
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core";
import { FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from "@angular/forms";
import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
import { UpdateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/update-client-organization.request";
import { ProviderPlanResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService, ToastService } from "@bitwarden/components";
type ManageClientSubscriptionDialogParams = {
organization: ProviderOrganizationOrganizationDetailsResponse;
provider: Provider;
};
export enum ManageClientSubscriptionDialogResultType {
Closed = "closed",
Submitted = "submitted",
}
export const openManageClientSubscriptionDialog = (
dialogService: DialogService,
dialogConfig: DialogConfig<ManageClientSubscriptionDialogParams>,
) =>
dialogService.open<
ManageClientSubscriptionDialogResultType,
ManageClientSubscriptionDialogParams
>(ManageClientSubscriptionDialogComponent, dialogConfig);
@Component({
templateUrl: "./manage-client-subscription-dialog.component.html",
})
export class ManageClientSubscriptionDialogComponent implements OnInit {
protected loading = true;
protected providerPlan: ProviderPlanResponse;
protected openSeats: number;
protected readonly ResultType = ManageClientSubscriptionDialogResultType;
protected formGroup = new FormGroup({
assignedSeats: new FormControl<number>(this.dialogParams.organization.seats, [
Validators.required,
Validators.min(0),
]),
});
constructor(
private billingApiService: BillingApiServiceAbstraction,
@Inject(DIALOG_DATA) protected dialogParams: ManageClientSubscriptionDialogParams,
private dialogRef: DialogRef<ManageClientSubscriptionDialogResultType>,
private i18nService: I18nService,
private toastService: ToastService,
) {}
async ngOnInit(): Promise<void> {
const response = await this.billingApiService.getProviderSubscription(
this.dialogParams.provider.id,
);
this.providerPlan = response.plans.find(
(plan) => plan.planName === this.dialogParams.organization.plan,
);
this.openSeats = this.providerPlan.seatMinimum - this.providerPlan.assignedSeats;
this.formGroup.controls.assignedSeats.addValidators(
this.isServiceUserWithPurchasedSeats
? this.createPurchasedSeatsValidator()
: this.createUnassignedSeatsValidator(),
);
this.loading = false;
}
submit = async () => {
this.loading = true;
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
return;
}
const request = new UpdateClientOrganizationRequest();
request.assignedSeats = this.formGroup.value.assignedSeats;
request.name = this.dialogParams.organization.organizationName;
await this.billingApiService.updateClientOrganization(
this.dialogParams.provider.id,
this.dialogParams.organization.id,
request,
);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("subscriptionUpdated"),
});
this.loading = false;
this.dialogRef.close(this.ResultType.Submitted);
};
createPurchasedSeatsValidator =
(): ValidatorFn =>
(formControl: FormControl<number>): ValidationErrors | null => {
if (this.isProviderAdmin) {
return null;
}
const seatAdjustment = formControl.value - this.dialogParams.organization.seats;
if (seatAdjustment <= 0) {
return null;
}
return {
insufficientPermissions: {
message: this.i18nService.t("contactYourProviderForAdditionalSeats"),
},
};
};
createUnassignedSeatsValidator =
(): ValidatorFn =>
(formControl: FormControl<number>): ValidationErrors | null => {
if (this.isProviderAdmin) {
return null;
}
const seatAdjustment = formControl.value - this.dialogParams.organization.seats;
if (seatAdjustment <= this.openSeats) {
return null;
}
const unassignedSeatsAvailableMessage = this.i18nService.t(
"unassignedSeatsAvailable",
this.openSeats,
);
const contactYourProviderMessage = this.i18nService.t(
"contactYourProviderForAdditionalSeats",
);
return {
insufficientPermissions: {
message: `${unassignedSeatsAvailableMessage} ${contactYourProviderMessage}`,
},
};
};
get unassignedSeats(): number {
const seatDifference =
this.formGroup.value.assignedSeats - this.dialogParams.organization.seats;
const unassignedSeats = this.openSeats - seatDifference;
return unassignedSeats >= 0 ? unassignedSeats : 0;
}
get additionalSeatsPurchased(): number {
const seatDifference =
this.formGroup.value.assignedSeats - this.dialogParams.organization.seats;
const purchasedSeats = seatDifference - this.openSeats;
return purchasedSeats > 0 ? purchasedSeats : 0;
}
get isProviderAdmin(): boolean {
return this.dialogParams.provider.type === ProviderUserType.ProviderAdmin;
}
get isServiceUserWithPurchasedSeats(): boolean {
return !this.isProviderAdmin && this.providerPlan && this.providerPlan.purchasedSeats > 0;
}
}

View File

@ -1,12 +1,6 @@
<app-header>
<bit-search [placeholder]="'search' | i18n" [(ngModel)]="searchText"></bit-search>
<a
type="button"
bitButton
*ngIf="manageOrganizations"
buttonType="primary"
(click)="createClientOrganization()"
>
<a type="button" bitButton *ngIf="isProviderAdmin" buttonType="primary" (click)="createClient()">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "addNewOrganization" | i18n }}
</a>
@ -73,15 +67,15 @@
appA11yTitle="{{ 'options' | i18n }}"
></button>
<bit-menu #rowMenu>
<button type="button" bitMenuItem (click)="manageName(client)">
<button type="button" bitMenuItem (click)="manageClientName(client)">
<i aria-hidden="true" class="bwi bwi-pencil-square"></i>
{{ "updateName" | i18n }}
</button>
<button type="button" bitMenuItem (click)="manageSubscription(client)">
<button type="button" bitMenuItem (click)="manageClientSubscription(client)">
<i aria-hidden="true" class="bwi bwi-family"></i>
{{ "manageSubscription" | i18n }}
</button>
<button type="button" bitMenuItem (click)="remove(client)">
<button *ngIf="this.isProviderAdmin" type="button" bitMenuItem (click)="remove(client)">
<span class="tw-text-danger">
<i aria-hidden="true" class="bwi bwi-close"></i> {{ "unlinkOrganization" | i18n }}
</span>
@ -92,6 +86,9 @@
</ng-template>
</bit-table>
<div *ngIf="clients.length === 0" class="tw-mt-10">
<app-no-clients (addNewOrganizationClicked)="createClientOrganization()" />
<app-no-clients
[showAddOrganizationButton]="this.isProviderAdmin"
(addNewOrganizationClicked)="createClient()"
/>
</div>
</ng-container>

View File

@ -10,7 +10,7 @@ import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { canAccessBilling } from "@bitwarden/common/billing/abstractions/provider-billing.service.abstraction";
import { hasConsolidatedBilling } from "@bitwarden/common/billing/abstractions/provider-billing.service.abstraction";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -21,24 +21,27 @@ import { BaseClientsComponent } from "../../../admin-console/providers/clients/b
import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service";
import {
CreateClientOrganizationResultType,
openCreateClientOrganizationDialog,
} from "./create-client-organization.component";
CreateClientDialogResultType,
openCreateClientDialog,
} from "./create-client-dialog.component";
import {
ManageClientOrganizationNameResultType,
openManageClientOrganizationNameDialog,
} from "./manage-client-organization-name.component";
import { ManageClientOrganizationSubscriptionComponent } from "./manage-client-organization-subscription.component";
ManageClientNameDialogResultType,
openManageClientNameDialog,
} from "./manage-client-name-dialog.component";
import {
ManageClientSubscriptionDialogResultType,
openManageClientSubscriptionDialog,
} from "./manage-client-subscription-dialog.component";
@Component({
templateUrl: "manage-client-organizations.component.html",
templateUrl: "manage-clients.component.html",
})
export class ManageClientOrganizationsComponent extends BaseClientsComponent {
export class ManageClientsComponent extends BaseClientsComponent {
providerId: string;
provider: Provider;
loading = true;
manageOrganizations = false;
isProviderAdmin = false;
protected plans: PlanResponse[];
@ -73,9 +76,9 @@ export class ManageClientOrganizationsComponent extends BaseClientsComponent {
switchMap((params) => {
this.providerId = params.providerId;
return this.providerService.get$(this.providerId).pipe(
canAccessBilling(this.configService),
map((canAccessBilling) => {
if (!canAccessBilling) {
hasConsolidatedBilling(this.configService),
map((hasConsolidatedBilling) => {
if (!hasConsolidatedBilling) {
return from(
this.router.navigate(["../clients"], {
relativeTo: this.activatedRoute,
@ -99,7 +102,7 @@ export class ManageClientOrganizationsComponent extends BaseClientsComponent {
async load() {
this.provider = await firstValueFrom(this.providerService.get$(this.providerId));
this.manageOrganizations = this.provider.type === ProviderUserType.ProviderAdmin;
this.isProviderAdmin = this.provider.type === ProviderUserType.ProviderAdmin;
this.clients = (await this.apiService.getProviderClients(this.providerId)).data;
@ -110,8 +113,23 @@ export class ManageClientOrganizationsComponent extends BaseClientsComponent {
this.loading = false;
}
async manageName(organization: ProviderOrganizationOrganizationDetailsResponse) {
const dialogRef = openManageClientOrganizationNameDialog(this.dialogService, {
createClient = async () => {
const reference = openCreateClientDialog(this.dialogService, {
data: {
providerId: this.providerId,
plans: this.plans,
},
});
const result = await lastValueFrom(reference.closed);
if (result === CreateClientDialogResultType.Submitted) {
await this.load();
}
};
manageClientName = async (organization: ProviderOrganizationOrganizationDetailsResponse) => {
const dialogRef = openManageClientNameDialog(this.dialogService, {
data: {
providerId: this.providerId,
organization: {
@ -124,38 +142,25 @@ export class ManageClientOrganizationsComponent extends BaseClientsComponent {
const result = await firstValueFrom(dialogRef.closed);
if (result === ManageClientOrganizationNameResultType.Submitted) {
if (result === ManageClientNameDialogResultType.Submitted) {
await this.load();
}
}
};
async manageSubscription(organization: ProviderOrganizationOrganizationDetailsResponse) {
if (organization == null) {
return;
}
const dialogRef = ManageClientOrganizationSubscriptionComponent.open(this.dialogService, {
organization: organization,
});
await firstValueFrom(dialogRef.closed);
await this.load();
}
createClientOrganization = async () => {
const reference = openCreateClientOrganizationDialog(this.dialogService, {
manageClientSubscription = async (
organization: ProviderOrganizationOrganizationDetailsResponse,
) => {
const dialogRef = openManageClientSubscriptionDialog(this.dialogService, {
data: {
providerId: this.providerId,
plans: this.plans,
organization,
provider: this.provider,
},
});
const result = await lastValueFrom(reference.closed);
const result = await firstValueFrom(dialogRef.closed);
if (result === CreateClientOrganizationResultType.Closed) {
return;
if (result === ManageClientSubscriptionDialogResultType.Submitted) {
await this.load();
}
await this.load();
};
}

View File

@ -1,4 +1,4 @@
import { Component, EventEmitter, Output } from "@angular/core";
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { svgIcon } from "@bitwarden/components";
@ -26,7 +26,13 @@ const gearIcon = svgIcon`
template: `<div class="tw-flex tw-flex-col tw-items-center tw-text-info">
<bit-icon [icon]="icon"></bit-icon>
<p class="tw-mt-4">{{ "noClients" | i18n }}</p>
<a type="button" bitButton buttonType="primary" (click)="addNewOrganization()">
<a
*ngIf="showAddOrganizationButton"
type="button"
bitButton
buttonType="primary"
(click)="addNewOrganization()"
>
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "addNewOrganization" | i18n }}
</a>
@ -34,6 +40,7 @@ const gearIcon = svgIcon`
})
export class NoClientsComponent {
icon = gearIcon;
@Input() showAddOrganizationButton = true;
@Output() addNewOrganizationClicked = new EventEmitter();
addNewOrganization = () => this.addNewOrganizationClicked.emit();

View File

@ -20,7 +20,6 @@ export const hasConsolidatedBilling: CanActivateFn = async (route: ActivatedRout
if (
!consolidatedBillingEnabled ||
!provider ||
!provider.isProviderAdmin ||
provider.providerStatus !== ProviderStatusType.Billable
) {
return createUrlTreeFromSnapshot(route, ["/providers", route.params.providerId]);

View File

@ -10,7 +10,7 @@
<ng-container header>
<tr>
<th bitCell>{{ "date" | i18n }}</th>
<th bitCell>{{ "invoiceNumberHeader" | i18n }}</th>
<th bitCell>{{ "invoice" | i18n }}</th>
<th bitCell>{{ "total" | i18n }}</th>
<th bitCell>{{ "status" | i18n }}</th>
</tr>
@ -29,7 +29,23 @@
</a>
</td>
<td bitCell>{{ invoice.total | currency: "$" }}</td>
<td bitCell>{{ invoice.status | titlecase }}</td>
<td bitCell *ngIf="expandInvoiceStatus(invoice) as expandedInvoiceStatus">
<span *ngIf="expandedInvoiceStatus === 'open'">
{{ "open" | i18n | titlecase }}
</span>
<span *ngIf="expandedInvoiceStatus === 'unpaid'">
<i class="bwi bwi-exclamation-circle tw-text-muted" aria-hidden="true"></i>
{{ "unpaid" | i18n | titlecase }}
</span>
<span *ngIf="expandedInvoiceStatus === 'paid'">
<i class="bwi bwi-check tw-text-success" aria-hidden="true"></i>
{{ "paid" | i18n | titlecase }}
</span>
<span *ngIf="expandedInvoiceStatus === 'uncollectible'">
<i class="bwi bwi-error tw-text-muted" aria-hidden="true"></i>
{{ "uncollectible" | i18n | titlecase }}
</span>
</td>
<td bitCell>
<button
[bitMenuTriggerFor]="rowMenu"

View File

@ -46,4 +46,19 @@ export class InvoicesComponent implements OnInit {
}
this.loading = false;
}
expandInvoiceStatus = (
invoice: InvoiceResponse,
): "open" | "unpaid" | "paid" | "uncollectible" => {
switch (invoice.status) {
case "open": {
const dueDate = new Date(invoice.dueDate);
return dueDate < new Date() ? "unpaid" : invoice.status;
}
case "paid":
case "uncollectible": {
return invoice.status;
}
}
};
}

View File

@ -7,7 +7,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
type MaybeProvider = Provider | undefined;
export const canAccessBilling = (
export const hasConsolidatedBilling = (
configService: ConfigService,
): OperatorFunction<MaybeProvider, boolean> =>
switchMap<MaybeProvider, Observable<boolean>>((provider) =>
@ -16,9 +16,7 @@ export const canAccessBilling = (
.pipe(
map((consolidatedBillingEnabled) =>
provider
? provider.isProviderAdmin &&
provider.providerStatus === ProviderStatusType.Billable &&
consolidatedBillingEnabled
? provider.providerStatus === ProviderStatusType.Billable && consolidatedBillingEnabled
: false,
),
),

View File

@ -18,6 +18,7 @@ export class InvoiceResponse extends BaseResponse {
number: string;
total: number;
status: string;
dueDate: string;
url: string;
pdfUrl: string;
@ -28,6 +29,7 @@ export class InvoiceResponse extends BaseResponse {
this.number = this.getResponseProperty("Number");
this.total = this.getResponseProperty("Total");
this.status = this.getResponseProperty("Status");
this.dueDate = this.getResponseProperty("DueDate");
this.url = this.getResponseProperty("Url");
this.pdfUrl = this.getResponseProperty("PdfUrl");
}

View File

@ -110,9 +110,11 @@ export class PasswordManagerPlanFeaturesResponse extends BaseResponse {
// Seats
stripePlanId: string;
stripeSeatPlanId: string;
stripeProviderPortalSeatPlanId: string;
stripePremiumAccessPlanId: string;
basePrice: number;
seatPrice: number;
providerPortalSeatPrice: number;
premiumAccessOptionPrice: number;
baseSeats: number;
maxAdditionalSeats: number;
@ -134,10 +136,14 @@ export class PasswordManagerPlanFeaturesResponse extends BaseResponse {
super(response);
this.stripePlanId = this.getResponseProperty("StripePlanId");
this.stripeSeatPlanId = this.getResponseProperty("StripeSeatPlanId");
this.stripeProviderPortalSeatPlanId = this.getResponseProperty(
"StripeProviderPortalSeatPlanId",
);
this.stripeStoragePlanId = this.getResponseProperty("StripeStoragePlanId");
this.stripePremiumAccessPlanId = this.getResponseProperty("StripePremiumAccessPlanId");
this.basePrice = this.getResponseProperty("BasePrice");
this.seatPrice = this.getResponseProperty("SeatPrice");
this.providerPortalSeatPrice = this.getResponseProperty("ProviderPortalSeatPrice");
this.baseSeats = this.getResponseProperty("BaseSeats");
this.maxAdditionalSeats = this.getResponseProperty("MaxAdditionalSeats");
this.premiumAccessOptionPrice = this.getResponseProperty("PremiumAccessOptionPrice");

212
package-lock.json generated
View File

@ -29,7 +29,7 @@
"@microsoft/signalr": "8.0.0",
"@microsoft/signalr-protocol-msgpack": "8.0.0",
"@ng-select/ng-select": "11.2.0",
"argon2": "0.31.0",
"argon2": "0.40.1",
"argon2-browser": "1.18.0",
"big-integer": "1.6.51",
"bootstrap": "4.6.0",
@ -67,7 +67,7 @@
"qrious": "4.0.2",
"rxjs": "7.8.1",
"tabbable": "6.2.0",
"tldts": "6.1.25",
"tldts": "6.1.29",
"utf-8-validate": "6.0.3",
"zone.js": "0.13.3",
"zxcvbn": "4.4.2"
@ -206,7 +206,7 @@
"dependencies": {
"@koa/multer": "3.0.2",
"@koa/router": "12.0.1",
"argon2": "0.31.0",
"argon2": "0.40.1",
"big-integer": "1.6.51",
"browser-hrtime": "1.1.8",
"chalk": "4.1.2",
@ -228,7 +228,7 @@
"papaparse": "5.4.1",
"proper-lockfile": "4.1.2",
"rxjs": "7.8.1",
"tldts": "6.1.25",
"tldts": "6.1.29",
"zxcvbn": "4.4.2"
},
"bin": {
@ -6666,90 +6666,6 @@
"node": ">=10"
}
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
"dependencies": {
"detect-libc": "^2.0.0",
"https-proxy-agent": "^5.0.0",
"make-dir": "^3.1.0",
"node-fetch": "^2.6.7",
"nopt": "^5.0.0",
"npmlog": "^5.0.1",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.11"
},
"bin": {
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@mapbox/node-pre-gyp/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/@mapbox/node-pre-gyp/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@mapbox/node-pre-gyp/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@mdx-js/react": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz",
@ -13397,7 +13313,8 @@
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"node_modules/abort-controller": {
"version": "3.0.0",
@ -13533,6 +13450,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"dependencies": {
"debug": "4"
},
@ -13886,7 +13804,8 @@
"node_modules/aproba": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
"dev": true
},
"node_modules/archiver": {
"version": "5.3.2",
@ -14013,18 +13932,6 @@
"integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==",
"dev": true
},
"node_modules/are-we-there-yet": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
@ -14032,17 +13939,17 @@
"dev": true
},
"node_modules/argon2": {
"version": "0.31.0",
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.31.0.tgz",
"integrity": "sha512-r56NWwlE3tjD/FIqL1T+V4Ka+Mb5yMF35w1YWHpwpEjeONXBUbxmjhWkWqY63mse8lpcZ+ZZIGpKL+s+qXhyfg==",
"version": "0.40.1",
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz",
"integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==",
"hasInstallScript": true,
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.11",
"@phc/format": "^1.0.0",
"node-addon-api": "^7.0.0"
"node-addon-api": "^7.1.0",
"node-gyp-build": "^4.8.0"
},
"engines": {
"node": ">=14.0.0"
"node": ">=16.17.0"
}
},
"node_modules/argon2-browser": {
@ -16064,6 +15971,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"dev": true,
"engines": {
"node": ">=10"
}
@ -16499,6 +16407,7 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"dev": true,
"bin": {
"color-support": "bin.js"
}
@ -16839,7 +16748,8 @@
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
"dev": true
},
"node_modules/constants-browserify": {
"version": "1.0.0",
@ -17939,6 +17849,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -21351,6 +21262,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dev": true,
"dependencies": {
"minipass": "^3.0.0"
},
@ -21362,6 +21274,7 @@
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@ -21372,7 +21285,8 @@
"node_modules/fs-minipass/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/fs-mkdirp-stream": {
"version": "1.0.0",
@ -21487,25 +21401,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gauge": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
"dependencies": {
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.2",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.1",
"object-assign": "^4.1.1",
"signal-exit": "^3.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@ -23023,7 +22918,8 @@
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
"dev": true
},
"node_modules/has-value": {
"version": "1.0.0",
@ -28158,6 +28054,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"dependencies": {
"semver": "^6.0.0"
},
@ -28172,6 +28069,7 @@
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
@ -29706,6 +29604,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dev": true,
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
@ -29718,6 +29617,7 @@
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@ -29728,7 +29628,8 @@
"node_modules/minizlib/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/mixin-deep": {
"version": "1.3.2",
@ -29747,6 +29648,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"bin": {
"mkdirp": "bin/cmd.js"
},
@ -30416,20 +30318,6 @@
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw=="
},
"node_modules/nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
"dependencies": {
"abbrev": "1"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/nord": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/nord/-/nord-0.2.1.tgz",
@ -30832,17 +30720,6 @@
"node": ">=8"
}
},
"node_modules/npmlog": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
"dependencies": {
"are-we-there-yet": "^2.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^3.0.0",
"set-blocking": "^2.0.0"
}
},
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@ -35284,7 +35161,8 @@
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true
},
"node_modules/set-cookie-parser": {
"version": "2.6.0",
@ -36817,6 +36695,7 @@
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"dev": true,
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@ -36867,6 +36746,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -36874,7 +36754,8 @@
"node_modules/tar/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/teex": {
"version": "1.0.1",
@ -37402,21 +37283,21 @@
"dev": true
},
"node_modules/tldts": {
"version": "6.1.25",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.25.tgz",
"integrity": "sha512-UmjB1dVArio9hny1D84VFeEvE37nCyfW5sWHr7AUV2MxJgxD8NR/kdmEMyjx5o/kRuOOBbaaXStce2R5C6I1Gg==",
"version": "6.1.29",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.29.tgz",
"integrity": "sha512-6VgFZeuDsC6hrAP+H18CIofrXbA1I7yHsHcMutwK39bEc2fmXrtsLFshV4bg5vza4xiUP4zyAWr9C48KiyxZVA==",
"license": "MIT",
"dependencies": {
"tldts-core": "^6.1.25"
"tldts-core": "^6.1.29"
},
"bin": {
"tldts": "bin/cli.js"
}
},
"node_modules/tldts-core": {
"version": "6.1.25",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.25.tgz",
"integrity": "sha512-hbSsjJOeDMV91JiqcrrFQ46D7EepH880zVmPjnBDmt3P+h0Aowz8Nh1adIcqkdhJbgpzZYQr6aM8/N3tZC6JyA==",
"version": "6.1.29",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.29.tgz",
"integrity": "sha512-ZhgwrF9P697hrsO8PZ4dFL8UZLLmczYcFwiknsPEk81BTC0xauqQfepPefIfS/YK2z2VVRQmyg0hZujShTlH7A==",
"license": "MIT"
},
"node_modules/tmp": {
@ -40043,6 +39924,7 @@
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dev": true,
"dependencies": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}

View File

@ -166,7 +166,7 @@
"@microsoft/signalr": "8.0.0",
"@microsoft/signalr-protocol-msgpack": "8.0.0",
"@ng-select/ng-select": "11.2.0",
"argon2": "0.31.0",
"argon2": "0.40.1",
"argon2-browser": "1.18.0",
"big-integer": "1.6.51",
"bootstrap": "4.6.0",
@ -204,7 +204,7 @@
"qrious": "4.0.2",
"rxjs": "7.8.1",
"tabbable": "6.2.0",
"tldts": "6.1.25",
"tldts": "6.1.29",
"utf-8-validate": "6.0.3",
"zone.js": "0.13.3",
"zxcvbn": "4.4.2"