diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2ef3d73e..9ffb70ff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -149,6 +149,20 @@ jobs: - name: Install Node dependencies run: npm ci + - name: Cache Native Module + uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2 + id: cache + with: + path: desktop_native/dist + key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }} + + - name: Build Native Module + if: steps.cache.outputs.cache-hit != 'true' + working-directory: './desktop_native' + run: | + npm ci + npm run build:cross-platform + - name: Build application run: npm run dist:lin @@ -228,11 +242,18 @@ jobs: shell: pwsh run: choco install checksum --no-progress + - name: Rust + shell: pwsh + run: | + rustup target install i686-pc-windows-msvc + rustup target install aarch64-pc-windows-msvc + - name: Print environment run: | node --version npm --version choco --version + rustup show - name: Login to Azure uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf @@ -253,6 +274,20 @@ jobs: - name: Install Node dependencies run: npm ci + - name: Cache Native Module + uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2 + id: cache + with: + path: desktop_native/dist + key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }} + + - name: Build Native Module + if: steps.cache.outputs.cache-hit != 'true' + working-directory: './desktop_native' + run: | + npm ci + npm run build:cross-platform + - name: Build & Sign (dev) env: ELECTRON_BUILDER_SIGN: 1 @@ -404,10 +439,15 @@ jobs: npm install -g node-gyp node-gyp install $(node -v) + - name: Rust + shell: pwsh + run: rustup target install aarch64-apple-darwin + - name: Print environment run: | node --version npm --version + rustup show echo "GitHub ref: $GITHUB_REF" echo "GitHub event: $GITHUB_EVENT" @@ -492,9 +532,23 @@ jobs: env: BUILD_NUMBER: ${{ needs.setup.outputs.build_number }} run: | - $package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\package.json | ConvertFrom-Json; - $package.build | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"; - $package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\package.json; + $package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\electron-builder.json | ConvertFrom-Json; + $package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"; + $package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\electron-builder.json; + + - name: Cache Native Module + uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2 + id: cache + with: + path: desktop_native/dist + key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }} + + - name: Build Native Module + if: steps.cache.outputs.cache-hit != 'true' + working-directory: './desktop_native' + run: | + npm ci + npm run build:cross-platform - name: Install Node dependencies run: npm ci @@ -548,10 +602,15 @@ jobs: npm install -g node-gyp node-gyp install $(node -v) + - name: Rust + shell: pwsh + run: rustup target install aarch64-apple-darwin + - name: Print environment run: | node --version npm --version + rustup show echo "GitHub ref: $GITHUB_REF" echo "GitHub event: $GITHUB_EVENT" @@ -636,9 +695,23 @@ jobs: env: BUILD_NUMBER: ${{ needs.setup.outputs.build_number }} run: | - $package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\package.json | ConvertFrom-Json; - $package.build | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"; - $package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\package.json; + $package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\electron-builder.json | ConvertFrom-Json; + $package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"; + $package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\electron-builder.json; + + - name: Cache Native Module + uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2 + id: cache + with: + path: desktop_native/dist + key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }} + + - name: Build Native Module + if: steps.cache.outputs.cache-hit != 'true' + working-directory: './desktop_native' + run: | + npm ci + npm run build:cross-platform - name: NPM install run: npm ci @@ -735,10 +808,15 @@ jobs: npm install -g node-gyp node-gyp install $(node -v) + - name: Rust + shell: pwsh + run: rustup target install aarch64-apple-darwin + - name: Print environment run: | node --version npm --version + rustup show echo "GitHub ref: $GITHUB_REF" echo "GitHub event: $GITHUB_EVENT" @@ -823,9 +901,23 @@ jobs: env: BUILD_NUMBER: ${{ needs.setup.outputs.build_number }} run: | - $package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\package.json | ConvertFrom-Json; - $package.build | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"; - $package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\package.json; + $package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\electron-builder.json | ConvertFrom-Json; + $package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"; + $package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\electron-builder.json; + + - name: Cache Native Module + uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2 + id: cache + with: + path: desktop_native/dist + key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }} + + - name: Build Native Module + if: steps.cache.outputs.cache-hit != 'true' + working-directory: './desktop_native' + run: | + npm ci + npm run build:cross-platform - name: NPM install run: npm ci @@ -1002,9 +1094,23 @@ jobs: env: BUILD_NUMBER: ${{ needs.setup.outputs.build_number }} run: | - $package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\package.json | ConvertFrom-Json; - $package.build | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"; - $package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\package.json; + $package = Get-Content -Raw -Path $env:GITHUB_WORKSPACE\electron-builder.json | ConvertFrom-Json; + $package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"; + $package | ConvertTo-Json -Depth 32 | Set-Content $env:GITHUB_WORKSPACE\electron-builder.json; + + - name: Cache Native Module + uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2 + id: cache + with: + path: desktop_native/dist + key: rust-${{ runner.os }}-${{ hashFiles('desktop_native/**/*') }} + + - name: Build Native Module + if: steps.cache.outputs.cache-hit != 'true' + working-directory: './desktop_native' + run: | + npm ci + npm run build:cross-platform - name: NPM install run: npm ci diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e978b16..8d8bb965 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,7 +63,7 @@ jobs: echo "::set-output name=branch-name::$BRANCH_NAME" - name: Download all artifacts - uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783 + uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8 with: workflow: build.yml workflow_conclusion: success @@ -142,7 +142,7 @@ jobs: run: mkdir dist - name: Download Snap artifact - uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783 + uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8 with: workflow: build.yml workflow_conclusion: success @@ -179,7 +179,7 @@ jobs: run: New-Item -ItemType directory -Path ./dist - name: Download choco artifact - uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783 + uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8 with: workflow: build.yml workflow_conclusion: success diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7dc1739c..a083eedb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ Here is how you can get involved: - **Write code for a new feature:** Make a new post in the [Github Contributions category](https://community.bitwarden.com/c/github-contributions/) of the Community Forums. Include a description of your proposed contribution, screeshots, and links to any relevant feature requests. This helps get feedback from the community and Bitwarden team members before you start writing code - **Report a bug or submit a bugfix:** Use Github issues and pull requests - **Write documentation:** Submit a pull request to the [Bitwarden help repository](https://github.com/bitwarden/help) -- **Help other users:** Go to the [User-to-User Support category](https://community.bitwarden.com/c/support/) on the Community Forums +- **Help other users:** Go to the [Ask the Bitwarden Community category](https://community.bitwarden.com/c/support/) on the Community Forums - **Translate:** See the localization (l10n) section below ## Contributor Agreement @@ -31,6 +31,6 @@ We use a translation tool called [Crowdin](https://crowdin.com) to help manage o If you are interested in helping translate the Bitwarden desktop app into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-desktop -If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/kspearrin). +If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/profile/dwbit). You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/ diff --git a/README.md b/README.md index 9020d9e4..d76e58db 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ [![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-desktop/localized.svg)](https://crowdin.com/project/bitwarden-desktop) [![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby) +> **Repository Reorganization in Progress** +> +> We are currently migrating some projects over to a mono repository. For existing PR's we will be providing documentation on how to move/migrate them. To minimize the overhead we are actively reviewing open PRs. If possible please ensure any pending comments are resolved as soon as possible. +> +> New pull requests created during this transition period may not get addressed —if needed, please create a new PR after the reorganization is complete. + # Bitwarden Desktop Application [![Platforms](https://imgur.com/SLv9paA.png "Windows, macOS, and Linux")](https://bitwarden.com/download/) @@ -12,7 +18,7 @@ The Bitwarden desktop app is written using Electron and Angular. The application # Build/Run -**Requirements** +## Requirements - [Node.js](https://nodejs.org) v16.13.1 (LTS) or greater - NPM v8 @@ -22,14 +28,24 @@ The Bitwarden desktop app is written using Electron and Angular. The application - Linux: - The following packages `build-essential libsecret-1-dev libglib2.0-dev` -**Run the app** +## Build native module + +The desktop application relies on native code written in rust, which needs to be compiled first. ```bash -npm install +cd desktop_native +npm ci +npm run build +``` + +## Run the app + +```bash +npm ci npm run electron ``` -**Debug Native Messaging** +### Debug Native Messaging Native Messaging (communication with the browser extension) works by having the browser start a lightweight proxy application baked into our desktop binary. To setup an environment which allows for easy debugging you will need to build the application for distribution, i.e. `npm run dist:`, start the dist version and enable desktop integration. This will write some manifests diff --git a/desktop_native/Cargo.lock b/desktop_native/Cargo.lock index 0382ada8..aec92d25 100644 --- a/desktop_native/Cargo.lock +++ b/desktop_native/Cargo.lock @@ -343,9 +343,9 @@ checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" [[package]] name = "libsecret" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8d814edb0cb306ffded3f1bfe72d6689529f95f7313a718331802723a73e5a" +checksum = "d4af5a2342942fa42d706a424e9f9914287fb8317132750fd73a241140ac38c1" dependencies = [ "bitflags", "gio", diff --git a/desktop_native/Cargo.toml b/desktop_native/Cargo.toml index 13f0bacb..dbc27602 100644 --- a/desktop_native/Cargo.toml +++ b/desktop_native/Cargo.toml @@ -40,4 +40,4 @@ security-framework-sys = "2.6.1" [target.'cfg(target_os = "linux")'.dependencies] gio = "0.15.6" -libsecret = "0.1.3" +libsecret = "0.1.4" diff --git a/desktop_native/build.js b/desktop_native/build.js new file mode 100644 index 00000000..ca42f7b0 --- /dev/null +++ b/desktop_native/build.js @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const child_process = require("child_process"); +const process = require("process"); + +let targets = []; +switch (process.platform) { + case "win32": + targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc"]; + break; + + case "darwin": + targets = ["x86_64-apple-darwin", "aarch64-apple-darwin"]; + break; + + default: + targets = ['x86_64-unknown-linux-gnu']; + break; +} + +targets.forEach(target => { + child_process.execSync(`npm run build -- --target ${target}`, {stdio: 'inherit'}); +}); diff --git a/desktop_native/index.js b/desktop_native/index.js new file mode 100644 index 00000000..572f3d60 --- /dev/null +++ b/desktop_native/index.js @@ -0,0 +1,114 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { readFileSync } = require('fs') + +const { platform, arch } = process + +let nativeBinding = null +let isMusl = false +let loadError = null + +switch (platform) { + case 'win32': + switch (arch) { + case 'x64': + try { + nativeBinding = require('./dist/desktop_native.win32-x64-msvc') + } catch (e) { + loadError = e + } + break + case 'ia32': + try { + nativeBinding = require('./dist/desktop_native.win32-ia32-msvc') + } catch (e) { + loadError = e + } + break + case 'arm64': + try { + nativeBinding = require('./dist/desktop_native.win32-arm64-msvc') + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on Windows: ${arch}`) + } + break + case 'darwin': + switch (arch) { + case 'x64': + try { + nativeBinding = require('./dist/desktop_native.darwin-x64') + } catch (e) { + loadError = e + } + break + case 'arm64': + try { + nativeBinding = require('./dist/desktop_native.darwin-arm64') + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on macOS: ${arch}`) + } + break + case 'linux': + switch (arch) { + case 'x64': + isMusl = readFileSync('/usr/bin/ldd', 'utf8').includes('musl') + if (isMusl) { + try { + nativeBinding = require('./dist/desktop_native.linux-x64-musl') + } catch (e) { + loadError = e + } + } else { + try { + nativeBinding = require('./dist/desktop_native.linux-x64-gnu') + } catch (e) { + loadError = e + } + } + break + case 'arm64': + isMusl = readFileSync('/usr/bin/ldd', 'utf8').includes('musl') + if (isMusl) { + try { + nativeBinding = require('./dist/desktop_native.linux-arm64-musl') + } catch (e) { + loadError = e + } + } else { + try { + nativeBinding = require('./dist/desktop_native.linux-arm64-gnu') + } catch (e) { + loadError = e + } + } + break + case 'arm': + try { + nativeBinding = require('./dist/desktop_native.linux-arm-gnueabihf') + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on Linux: ${arch}`) + } + break + default: + throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) +} + +if (!nativeBinding) { + if (loadError) { + throw loadError + } + throw new Error(`Failed to load native binding`) +} + +module.exports.nativeBinding diff --git a/desktop_native/package-lock.json b/desktop_native/package-lock.json index b72bb80c..70e590de 100644 --- a/desktop_native/package-lock.json +++ b/desktop_native/package-lock.json @@ -1,23 +1,22 @@ { - "name": "desktop_native", + "name": "@bitwarden/desktop_native", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "desktop_native", + "name": "@bitwarden/desktop_native", "version": "0.1.0", "hasInstallScript": true, "license": "GPL-3.0", "devDependencies": { - "@napi-rs/cli": "^2.4.4", - "cargo-cp-artifact": "^0.1" + "@napi-rs/cli": "^2.6.2" } }, "node_modules/@napi-rs/cli": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.4.4.tgz", - "integrity": "sha512-f+tvwCv1ka24dBqI2DgBhR7Oxl3DKHOp4onxLXwyBFt6iCADnr3YZIr1/2Iq5r3uqxFgaf01bfPsRQZPkEp0kQ==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.6.2.tgz", + "integrity": "sha512-EmH+DQDEBUIoqMim0cc+X96ImtcIZLFjgW5WWORpzYnA9Ug7zNPO7jCLMhIQRd/p5AdWaXrT4SVXc/aip09rKQ==", "dev": true, "bin": { "napi": "scripts/index.js" @@ -29,28 +28,13 @@ "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } - }, - "node_modules/cargo-cp-artifact": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz", - "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==", - "dev": true, - "bin": { - "cargo-cp-artifact": "bin/cargo-cp-artifact.js" - } } }, "dependencies": { "@napi-rs/cli": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.4.4.tgz", - "integrity": "sha512-f+tvwCv1ka24dBqI2DgBhR7Oxl3DKHOp4onxLXwyBFt6iCADnr3YZIr1/2Iq5r3uqxFgaf01bfPsRQZPkEp0kQ==", - "dev": true - }, - "cargo-cp-artifact": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.6.tgz", - "integrity": "sha512-CQw0doK/aaF7j041666XzuilHxqMxaKkn+I5vmBsd8SAwS0cO5CqVEVp0xJwOKstyqWZ6WK4Ww3O6p26x/Goyg==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.6.2.tgz", + "integrity": "sha512-EmH+DQDEBUIoqMim0cc+X96ImtcIZLFjgW5WWORpzYnA9Ug7zNPO7jCLMhIQRd/p5AdWaXrT4SVXc/aip09rKQ==", "dev": true } } diff --git a/desktop_native/package.json b/desktop_native/package.json index 4ddb2758..7fe4c970 100644 --- a/desktop_native/package.json +++ b/desktop_native/package.json @@ -1,19 +1,32 @@ { - "name": "desktop_native", + "name": "@bitwarden/desktop_native", "version": "0.1.0", "description": "", "main": "index.node", "scripts": { - "build": "napi build --release --js true", - "build-debug": "npm run build --", - "build-release": "npm run build -- --release", - "install": "npm run build-release", + "build": "napi build dist --platform --release --js true", + "build:debug": "napi build dist --platform --js true", + "build:cross-platform": "node build.js", "test": "cargo test" }, "author": "", "license": "GPL-3.0", "devDependencies": { - "@napi-rs/cli": "^2.4.4", - "cargo-cp-artifact": "^0.1" + "@napi-rs/cli": "^2.6.2" + }, + "napi": { + "name": "desktop_native", + "triples": { + "defaults": true, + "additional": [ + "x86_64-unknown-linux-musl", + "aarch64-unknown-linux-gnu", + "i686-pc-windows-msvc", + "armv7-unknown-linux-gnueabihf", + "aarch64-apple-darwin", + "aarch64-unknown-linux-musl", + "aarch64-pc-windows-msvc" + ] + } } } diff --git a/electron-builder.json b/electron-builder.json new file mode 100644 index 00000000..e92d20a8 --- /dev/null +++ b/electron-builder.json @@ -0,0 +1,121 @@ +{ + "extraMetadata": { "name": "bitwarden" }, + "productName": "Bitwarden", + "appId": "com.bitwarden.desktop", + "buildDependenciesFromSource": true, + "copyright": "Copyright © 2015-2022 Bitwarden Inc.", + "directories": { "buildResources": "resources", "output": "dist", "app": "build" }, + "afterSign": "scripts/after-sign.js", + "asarUnpack": ["**/*.node"], + "files": [ + "**/*", + "!**/node_modules/@bitwarden/desktop-native/**/*", + "**/node_modules/@bitwarden/desktop-native/index.js", + "**/node_modules/@bitwarden/desktop-native/dist/desktop_native.${platform}-${arch}*.node" + ], + "mac": { + "electronUpdaterCompatibility": ">=0.0.1", + "category": "public.app-category.productivity", + "darkModeSupport": true, + "gatekeeperAssess": false, + "hardenedRuntime": true, + "entitlements": "resources/entitlements.mac.plist", + "entitlementsInherit": "resources/entitlements.mac.plist", + "extendInfo": { + "ITSAppUsesNonExemptEncryption": false, + "CFBundleLocalizations": [ + "en", + "cs", + "da", + "de", + "es", + "et", + "fi", + "fr", + "hr", + "hu", + "id", + "it", + "ja", + "nb", + "nl", + "pl", + "pt-BR", + "pt-PT", + "ro", + "ru", + "sk", + "sv", + "tr", + "uk", + "vi", + "zh-Hans", + "zh-Hant" + ], + "CFBundleDevelopmentRegion": "en" + }, + "target": ["dmg", "zip"] + }, + "win": { + "electronUpdaterCompatibility": ">=0.0.1", + "target": ["portable", "nsis-web", "appx"], + "sign": "./sign.js", + "extraResources": [ + { "from": "node_modules/regedit/vbs", "to": "regedit/vbs", "filter": ["**/*"] }, + { "from": "resources/native-messaging.bat", "to": "native-messaging.bat" } + ] + }, + "linux": { + "category": "Utility", + "synopsis": "A secure and free password manager for all of your devices.", + "target": ["deb", "freebsd", "rpm", "AppImage", "snap"], + "desktop": { "Name": "Bitwarden", "Type": "Application", "GenericName": "Password Manager" } + }, + "dmg": { + "icon": "dmg.icns", + "contents": [ + { "x": 150, "y": 185, "type": "file" }, + { "x": 390, "y": 180, "type": "link", "path": "/Applications" } + ], + "window": { "width": 540, "height": 380 } + }, + "mas": { + "entitlements": "resources/entitlements.mas.plist", + "entitlementsInherit": "resources/entitlements.mas.inherit.plist", + "hardenedRuntime": false, + "extendInfo": { "LSMinimumSystemVersion": "10.14.0" } + }, + "nsisWeb": { + "oneClick": false, + "perMachine": false, + "allowToChangeInstallationDirectory": false, + "artifactName": "${productName}-Installer-${version}.${ext}", + "uninstallDisplayName": "${productName}", + "deleteAppDataOnUninstall": true + }, + "portable": { "artifactName": "${productName}-Portable-${version}.${ext}" }, + "appx": { + "artifactName": "${productName}-${version}-${arch}.${ext}", + "backgroundColor": "#175DDC", + "applicationId": "bitwardendesktop", + "identityName": "8bitSolutionsLLC.bitwardendesktop", + "publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418", + "publisherDisplayName": "8bit Solutions LLC", + "languages": ["en-US"] + }, + "deb": { + "artifactName": "${productName}-${version}-${arch}.${ext}", + "depends": ["libnotify4", "libxtst6", "libnss3", "libsecret-1-0", "libxss1"] + }, + "appImage": { "artifactName": "${productName}-${version}-${arch}.${ext}" }, + "rpm": { "artifactName": "${productName}-${version}-${arch}.${ext}" }, + "freebsd": { "artifactName": "${productName}-${version}-${arch}.${ext}" }, + "snap": { + "autoStart": true, + "confinement": "strict", + "plugs": ["default", "password-manager-service"], + "stagePackages": ["default"], + "publish": ["github"] + }, + "protocols": [{ "name": "Bitwarden", "schemes": ["bitwarden"] }] +} diff --git a/jslib b/jslib index f3a4fde5..d7e55465 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit f3a4fde513dc16779e85597bd00027b160671db7 +Subproject commit d7e554653a7e593f8cdaf7e2fe926eb04fb5d5c5 diff --git a/package-lock.json b/package-lock.json index 134babd1..fe7887aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,7 +77,7 @@ } }, "desktop_native": { - "name": "desktop_native", + "name": "@bitwarden/desktop_native", "version": "0.1.0", "hasInstallScript": true, "license": "GPL-3.0", @@ -87,6 +87,7 @@ } }, "jslib/angular": { + "name": "@bitwarden/jslib-angular", "version": "0.0.0", "license": "GPL-3.0", "dependencies": { @@ -112,6 +113,7 @@ } }, "jslib/common": { + "name": "@bitwarden/jslib-common", "version": "0.0.0", "license": "GPL-3.0", "dependencies": { @@ -138,6 +140,7 @@ } }, "jslib/electron": { + "name": "@bitwarden/jslib-electron", "version": "0.0.0", "license": "GPL-3.0", "dependencies": { @@ -2748,9 +2751,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001325", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz", - "integrity": "sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ==", + "version": "1.0.30001327", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001327.tgz", + "integrity": "sha512-1/Cg4jlD9qjZzhbzkzEaAC2JHsP0WrOc8Rd/3a3LuajGzGWR/hD7TVyvq99VqmTy99eVh8Zkmdq213OgvgXx7w==", "dev": true, "funding": [ { @@ -9678,9 +9681,9 @@ } }, "node_modules/semver/node_modules/lru-cache": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.3.tgz", - "integrity": "sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.0.tgz", + "integrity": "sha512-AmXqneQZL3KZMIgBpaPTeI6pfwh+xQ2vutMsyqOu1TBdEXFZgpG/80wuJ531w2ZN7TI0/oc8CPxzh/DKQudZqg==", "engines": { "node": ">=12" } @@ -13422,9 +13425,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001325", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz", - "integrity": "sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ==", + "version": "1.0.30001327", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001327.tgz", + "integrity": "sha512-1/Cg4jlD9qjZzhbzkzEaAC2JHsP0WrOc8Rd/3a3LuajGzGWR/hD7TVyvq99VqmTy99eVh8Zkmdq213OgvgXx7w==", "dev": true }, "canonical-path": { @@ -18533,9 +18536,9 @@ }, "dependencies": { "lru-cache": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.7.3.tgz", - "integrity": "sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw==" + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.0.tgz", + "integrity": "sha512-AmXqneQZL3KZMIgBpaPTeI6pfwh+xQ2vutMsyqOu1TBdEXFZgpG/80wuJ531w2ZN7TI0/oc8CPxzh/DKQudZqg==" } } }, diff --git a/package.json b/package.json index 6c1b1909..f735bd60 100644 --- a/package.json +++ b/package.json @@ -63,203 +63,6 @@ "prettier": "prettier --write .", "prepare": "husky install" }, - "build": { - "extraMetadata": { - "name": "bitwarden" - }, - "productName": "Bitwarden", - "appId": "com.bitwarden.desktop", - "buildDependenciesFromSource": true, - "copyright": "Copyright © 2015-2022 Bitwarden Inc.", - "directories": { - "buildResources": "resources", - "output": "dist", - "app": "build" - }, - "afterSign": "scripts/after-sign.js", - "asarUnpack": [ - "**/*.node" - ], - "files": [ - "**/*", - "!**/node_modules/@bitwarden/desktop-native/**/*" - ], - "mac": { - "electronUpdaterCompatibility": ">=0.0.1", - "category": "public.app-category.productivity", - "darkModeSupport": true, - "gatekeeperAssess": false, - "hardenedRuntime": true, - "entitlements": "resources/entitlements.mac.plist", - "entitlementsInherit": "resources/entitlements.mac.plist", - "extendInfo": { - "ITSAppUsesNonExemptEncryption": false, - "CFBundleLocalizations": [ - "en", - "cs", - "da", - "de", - "es", - "et", - "fi", - "fr", - "hr", - "hu", - "id", - "it", - "ja", - "nb", - "nl", - "pl", - "pt-BR", - "pt-PT", - "ro", - "ru", - "sk", - "sv", - "tr", - "uk", - "vi", - "zh-Hans", - "zh-Hant" - ], - "CFBundleDevelopmentRegion": "en" - }, - "target": [ - "dmg", - "zip" - ] - }, - "win": { - "electronUpdaterCompatibility": ">=0.0.1", - "target": [ - "portable", - "nsis-web", - "appx" - ], - "sign": "./sign.js", - "extraResources": [ - { - "from": "node_modules/regedit/vbs", - "to": "regedit/vbs", - "filter": [ - "**/*" - ] - }, - { - "from": "resources/native-messaging.bat", - "to": "native-messaging.bat" - } - ] - }, - "linux": { - "category": "Utility", - "synopsis": "A secure and free password manager for all of your devices.", - "target": [ - "deb", - "freebsd", - "rpm", - "AppImage", - "snap" - ], - "desktop": { - "Name": "Bitwarden", - "Type": "Application", - "GenericName": "Password Manager" - } - }, - "dmg": { - "icon": "dmg.icns", - "contents": [ - { - "x": 150, - "y": 185, - "type": "file" - }, - { - "x": 390, - "y": 180, - "type": "link", - "path": "/Applications" - } - ], - "window": { - "width": 540, - "height": 380 - } - }, - "mas": { - "entitlements": "resources/entitlements.mas.plist", - "entitlementsInherit": "resources/entitlements.mas.inherit.plist", - "hardenedRuntime": false, - "extendInfo": { - "LSMinimumSystemVersion": "10.14.0" - } - }, - "nsisWeb": { - "oneClick": false, - "perMachine": false, - "allowToChangeInstallationDirectory": false, - "artifactName": "${productName}-Installer-${version}.${ext}", - "uninstallDisplayName": "${productName}", - "deleteAppDataOnUninstall": true - }, - "portable": { - "artifactName": "${productName}-Portable-${version}.${ext}" - }, - "appx": { - "artifactName": "${productName}-${version}-${arch}.${ext}", - "backgroundColor": "#175DDC", - "applicationId": "bitwardendesktop", - "identityName": "8bitSolutionsLLC.bitwardendesktop", - "publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418", - "publisherDisplayName": "8bit Solutions LLC", - "languages": [ - "en-US" - ] - }, - "deb": { - "artifactName": "${productName}-${version}-${arch}.${ext}", - "depends": [ - "libnotify4", - "libxtst6", - "libnss3", - "libsecret-1-0", - "libxss1" - ] - }, - "appImage": { - "artifactName": "${productName}-${version}-${arch}.${ext}" - }, - "rpm": { - "artifactName": "${productName}-${version}-${arch}.${ext}" - }, - "freebsd": { - "artifactName": "${productName}-${version}-${arch}.${ext}" - }, - "snap": { - "autoStart": true, - "confinement": "strict", - "plugs": [ - "default", - "password-manager-service" - ], - "stagePackages": [ - "default" - ], - "publish": [ - "github" - ] - }, - "protocols": [ - { - "name": "Bitwarden", - "schemes": [ - "bitwarden" - ] - } - ] - }, "devDependencies": { "@angular/compiler-cli": "^12.2.13", "@ngtools/webpack": "^12.2.13", diff --git a/src/app/accounts/two-factor.component.ts b/src/app/accounts/two-factor.component.ts index db482230..acf8919f 100644 --- a/src/app/accounts/two-factor.component.ts +++ b/src/app/accounts/two-factor.component.ts @@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from "@angular/router"; import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component"; import { ModalService } from "jslib-angular/services/modal.service"; import { ApiService } from "jslib-common/abstractions/api.service"; +import { AppIdService } from "jslib-common/abstractions/appId.service"; import { AuthService } from "jslib-common/abstractions/auth.service"; import { EnvironmentService } from "jslib-common/abstractions/environment.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; @@ -38,7 +39,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { stateService: StateService, route: ActivatedRoute, logService: LogService, - twoFactorService: TwoFactorService + twoFactorService: TwoFactorService, + appIdService: AppIdService ) { super( authService, @@ -51,7 +53,8 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { stateService, route, logService, - twoFactorService + twoFactorService, + appIdService ); super.onSuccessfulLogin = () => { return syncService.fullSync(true); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 037117f4..29e324cf 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -83,7 +83,7 @@ import { AppRoutingModule } from "./app-routing.module"; import { AppComponent } from "./app.component"; import { PasswordRepromptComponent } from "./components/password-reprompt.component"; import { SetPinComponent } from "./components/set-pin.component"; -import { VerifyMasterPasswordComponent } from "./components/verify-master-password.component"; +import { UserVerificationComponent } from "./components/user-verification.component"; import { AccountSwitcherComponent } from "./layout/account-switcher.component"; import { HeaderComponent } from "./layout/header.component"; import { NavComponent } from "./layout/nav.component"; @@ -91,7 +91,7 @@ import { SearchComponent } from "./layout/search/search.component"; import { AddEditComponent as SendAddEditComponent } from "./send/add-edit.component"; import { EffluxDatesComponent as SendEffluxDatesComponent } from "./send/efflux-dates.component"; import { SendComponent } from "./send/send.component"; -import { ServicesModule } from "./services.module"; +import { ServicesModule } from "./services/services.module"; import { AddEditCustomFieldsComponent } from "./vault/add-edit-custom-fields.component"; import { AddEditComponent } from "./vault/add-edit.component"; import { AttachmentsComponent } from "./vault/attachments.component"; @@ -212,9 +212,9 @@ registerLocaleData(localeZhTw, "zh-TW"); TwoFactorComponent, TwoFactorOptionsComponent, UpdateTempPasswordComponent, + UserVerificationComponent, VaultComponent, VaultTimeoutInputComponent, - VerifyMasterPasswordComponent, ViewComponent, ViewCustomFieldsComponent, ], diff --git a/src/app/components/verify-master-password.component.html b/src/app/components/user-verification.component.html similarity index 100% rename from src/app/components/verify-master-password.component.html rename to src/app/components/user-verification.component.html diff --git a/src/app/components/verify-master-password.component.ts b/src/app/components/user-verification.component.ts similarity index 55% rename from src/app/components/verify-master-password.component.ts rename to src/app/components/user-verification.component.ts index 56b36505..dc12c94a 100644 --- a/src/app/components/verify-master-password.component.ts +++ b/src/app/components/user-verification.component.ts @@ -2,16 +2,16 @@ import { animate, style, transition, trigger } from "@angular/animations"; import { Component } from "@angular/core"; import { NG_VALUE_ACCESSOR } from "@angular/forms"; -import { VerifyMasterPasswordComponent as BaseComponent } from "jslib-angular/components/verify-master-password.component"; +import { UserVerificationComponent as BaseComponent } from "jslib-angular/components/user-verification.component"; @Component({ - selector: "app-verify-master-password", - templateUrl: "verify-master-password.component.html", + selector: "app-user-verification", + templateUrl: "user-verification.component.html", providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, - useExisting: VerifyMasterPasswordComponent, + useExisting: UserVerificationComponent, }, ], animations: [ @@ -20,4 +20,4 @@ import { VerifyMasterPasswordComponent as BaseComponent } from "jslib-angular/co ]), ], }) -export class VerifyMasterPasswordComponent extends BaseComponent {} +export class UserVerificationComponent extends BaseComponent {} diff --git a/src/app/services.module.ts b/src/app/services.module.ts deleted file mode 100644 index 74acaabc..00000000 --- a/src/app/services.module.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { APP_INITIALIZER, NgModule } from "@angular/core"; - -import { JslibServicesModule } from "jslib-angular/services/jslib-services.module"; -import { BroadcasterService as BroadcasterServiceAbstraction } from "jslib-common/abstractions/broadcaster.service"; -import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service"; -import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "jslib-common/abstractions/cryptoFunction.service"; -import { EnvironmentService as EnvironmentServiceAbstraction } from "jslib-common/abstractions/environment.service"; -import { EventService as EventServiceAbstraction } from "jslib-common/abstractions/event.service"; -import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service"; -import { LogService as LogServiceAbstraction } from "jslib-common/abstractions/log.service"; -import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service"; -import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service"; -import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service"; -import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service"; -import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service"; -import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service"; -import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service"; -import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service"; -import { SystemService as SystemServiceAbstraction } from "jslib-common/abstractions/system.service"; -import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service"; -import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service"; -import { ThemeType } from "jslib-common/enums/themeType"; -import { StateFactory } from "jslib-common/factories/stateFactory"; -import { GlobalState } from "jslib-common/models/domain/globalState"; -import { ContainerService } from "jslib-common/services/container.service"; -import { EventService } from "jslib-common/services/event.service"; -import { StateMigrationService } from "jslib-common/services/stateMigration.service"; -import { SystemService } from "jslib-common/services/system.service"; -import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service"; -import { ElectronCryptoService } from "jslib-electron/services/electronCrypto.service"; -import { ElectronLogService } from "jslib-electron/services/electronLog.service"; -import { ElectronPlatformUtilsService } from "jslib-electron/services/electronPlatformUtils.service"; -import { ElectronRendererMessagingService } from "jslib-electron/services/electronRendererMessaging.service"; -import { ElectronRendererSecureStorageService } from "jslib-electron/services/electronRendererSecureStorage.service"; -import { ElectronRendererStorageService } from "jslib-electron/services/electronRendererStorage.service"; - -import { Account } from "../models/account"; -import { I18nService } from "../services/i18n.service"; -import { LoginGuardService } from "../services/loginGuard.service"; -import { NativeMessagingService } from "../services/nativeMessaging.service"; -import { PasswordRepromptService } from "../services/passwordReprompt.service"; -import { StateService } from "../services/state.service"; - -import { SearchBarService } from "./layout/search/search-bar.service"; - -export function initFactory( - window: Window, - environmentService: EnvironmentServiceAbstraction, - syncService: SyncServiceAbstraction, - vaultTimeoutService: VaultTimeoutService, - i18nService: I18nService, - eventService: EventService, - twoFactorService: TwoFactorServiceAbstraction, - notificationsService: NotificationsServiceAbstraction, - platformUtilsService: PlatformUtilsServiceAbstraction, - stateService: StateServiceAbstraction, - cryptoService: CryptoServiceAbstraction, - nativeMessagingService: NativeMessagingService -): () => Promise { - return async () => { - nativeMessagingService.init(); - await stateService.init(); - await environmentService.setUrlsFromStorage(); - syncService.fullSync(true); - await vaultTimeoutService.init(true); - const locale = await stateService.getLocale(); - await i18nService.init(locale); - eventService.init(true); - twoFactorService.init(); - setTimeout(() => notificationsService.init(), 3000); - const htmlEl = window.document.documentElement; - htmlEl.classList.add("os_" + platformUtilsService.getDeviceString()); - htmlEl.classList.add("locale_" + i18nService.translationLocale); - const theme = await platformUtilsService.getEffectiveTheme(); - htmlEl.classList.add("theme_" + theme); - platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => { - const bwTheme = await stateService.getTheme(); - if (bwTheme == null || bwTheme === ThemeType.System) { - htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark); - htmlEl.classList.add("theme_" + sysTheme); - } - }); - - let installAction = null; - const installedVersion = await stateService.getInstalledVersion(); - const currentVersion = await platformUtilsService.getApplicationVersion(); - if (installedVersion == null) { - installAction = "install"; - } else if (installedVersion !== currentVersion) { - installAction = "update"; - } - - if (installAction != null) { - await stateService.setInstalledVersion(currentVersion); - } - - const containerService = new ContainerService(cryptoService); - containerService.attachToGlobal(window); - }; -} - -@NgModule({ - imports: [JslibServicesModule], - declarations: [], - providers: [ - { - provide: APP_INITIALIZER, - useFactory: initFactory, - deps: [ - "WINDOW", - EnvironmentServiceAbstraction, - SyncServiceAbstraction, - VaultTimeoutServiceAbstraction, - I18nServiceAbstraction, - EventServiceAbstraction, - TwoFactorServiceAbstraction, - NotificationsServiceAbstraction, - PlatformUtilsServiceAbstraction, - StateServiceAbstraction, - CryptoServiceAbstraction, - NativeMessagingService, - ], - multi: true, - }, - { provide: LogServiceAbstraction, useClass: ElectronLogService, deps: [] }, - { - provide: PlatformUtilsServiceAbstraction, - useFactory: ( - i18nService: I18nServiceAbstraction, - messagingService: MessagingServiceAbstraction, - stateService: StateServiceAbstraction - ) => new ElectronPlatformUtilsService(i18nService, messagingService, true, stateService), - deps: [I18nServiceAbstraction, MessagingServiceAbstraction, StateServiceAbstraction], - }, - { - provide: I18nServiceAbstraction, - useFactory: (window: Window) => new I18nService(window.navigator.language, "./locales"), - deps: ["WINDOW"], - }, - { - provide: MessagingServiceAbstraction, - useClass: ElectronRendererMessagingService, - deps: [BroadcasterServiceAbstraction], - }, - { provide: StorageServiceAbstraction, useClass: ElectronRendererStorageService }, - { provide: "SECURE_STORAGE", useClass: ElectronRendererSecureStorageService }, - { - provide: CryptoServiceAbstraction, - useClass: ElectronCryptoService, - deps: [ - CryptoFunctionServiceAbstraction, - PlatformUtilsServiceAbstraction, - LogServiceAbstraction, - StateServiceAbstraction, - ], - }, - { - provide: SystemServiceAbstraction, - useFactory: ( - messagingService: MessagingServiceAbstraction, - platformUtilsService: PlatformUtilsServiceAbstraction, - stateService: StateServiceAbstraction - ) => new SystemService(messagingService, platformUtilsService, null, stateService), - deps: [MessagingServiceAbstraction, PlatformUtilsServiceAbstraction, StateServiceAbstraction], - }, - { provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService }, - NativeMessagingService, - SearchBarService, - { - provide: LoginGuardService, - useClass: LoginGuardService, - deps: [StateServiceAbstraction, PlatformUtilsServiceAbstraction, I18nServiceAbstraction], - }, - { - provide: StateServiceAbstraction, - useFactory: ( - storageService: StorageServiceAbstraction, - secureStorageService: StorageServiceAbstraction, - logService: LogServiceAbstraction, - stateMigrationService: StateMigrationServiceAbstraction - ) => - new StateService( - storageService, - secureStorageService, - logService, - stateMigrationService, - new StateFactory(GlobalState, Account) - ), - deps: [ - StorageServiceAbstraction, - "SECURE_STORAGE", - LogServiceAbstraction, - StateMigrationServiceAbstraction, - ], - }, - { - provide: StateMigrationServiceAbstraction, - useFactory: ( - storageService: StorageServiceAbstraction, - secureStorageService: StorageServiceAbstraction - ) => - new StateMigrationService( - storageService, - secureStorageService, - new StateFactory(GlobalState, Account) - ), - deps: [StorageServiceAbstraction, "SECURE_STORAGE"], - }, - ], -}) -export class ServicesModule {} diff --git a/src/app/services/init.service.ts b/src/app/services/init.service.ts new file mode 100644 index 00000000..3bc3d49a --- /dev/null +++ b/src/app/services/init.service.ts @@ -0,0 +1,81 @@ +import { Inject, Injectable } from "@angular/core"; + +import { WINDOW } from "jslib-angular/services/jslib-services.module"; +import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service"; +import { EnvironmentService as EnvironmentServiceAbstraction } from "jslib-common/abstractions/environment.service"; +import { EventService as EventServiceAbstraction } from "jslib-common/abstractions/event.service"; +import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service"; +import { NotificationsService as NotificationsServiceAbstraction } from "jslib-common/abstractions/notifications.service"; +import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service"; +import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service"; +import { SyncService as SyncServiceAbstraction } from "jslib-common/abstractions/sync.service"; +import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service"; +import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service"; +import { ThemeType } from "jslib-common/enums/themeType"; +import { ContainerService } from "jslib-common/services/container.service"; +import { EventService } from "jslib-common/services/event.service"; +import { VaultTimeoutService } from "jslib-common/services/vaultTimeout.service"; + +import { I18nService } from "../../services/i18n.service"; +import { NativeMessagingService } from "../../services/nativeMessaging.service"; + +@Injectable() +export class InitService { + constructor( + @Inject(WINDOW) private win: Window, + private environmentService: EnvironmentServiceAbstraction, + private syncService: SyncServiceAbstraction, + private vaultTimeoutService: VaultTimeoutServiceAbstraction, + private i18nService: I18nServiceAbstraction, + private eventService: EventServiceAbstraction, + private twoFactorService: TwoFactorServiceAbstraction, + private notificationsService: NotificationsServiceAbstraction, + private platformUtilsService: PlatformUtilsServiceAbstraction, + private stateService: StateServiceAbstraction, + private cryptoService: CryptoServiceAbstraction, + private nativeMessagingService: NativeMessagingService + ) {} + + init() { + return async () => { + this.nativeMessagingService.init(); + await this.stateService.init(); + await this.environmentService.setUrlsFromStorage(); + this.syncService.fullSync(true); + (this.vaultTimeoutService as VaultTimeoutService).init(true); + const locale = await this.stateService.getLocale(); + await (this.i18nService as I18nService).init(locale); + (this.eventService as EventService).init(true); + this.twoFactorService.init(); + setTimeout(() => this.notificationsService.init(), 3000); + const htmlEl = this.win.document.documentElement; + htmlEl.classList.add("os_" + this.platformUtilsService.getDeviceString()); + + const theme = await this.platformUtilsService.getEffectiveTheme(); + htmlEl.classList.add("theme_" + theme); + this.platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => { + const bwTheme = await this.stateService.getTheme(); + if (bwTheme == null || bwTheme === ThemeType.System) { + htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark); + htmlEl.classList.add("theme_" + sysTheme); + } + }); + + let installAction = null; + const installedVersion = await this.stateService.getInstalledVersion(); + const currentVersion = await this.platformUtilsService.getApplicationVersion(); + if (installedVersion == null) { + installAction = "install"; + } else if (installedVersion !== currentVersion) { + installAction = "update"; + } + + if (installAction != null) { + await this.stateService.setInstalledVersion(currentVersion); + } + + const containerService = new ContainerService(this.cryptoService); + containerService.attachToGlobal(this.win); + }; + } +} diff --git a/src/app/services/services.module.ts b/src/app/services/services.module.ts new file mode 100644 index 00000000..694cc6de --- /dev/null +++ b/src/app/services/services.module.ts @@ -0,0 +1,135 @@ +import { APP_INITIALIZER, InjectionToken, NgModule } from "@angular/core"; + +import { + JslibServicesModule, + SECURE_STORAGE, + STATE_FACTORY, + STATE_SERVICE_USE_CACHE, + WINDOW, + CLIENT_TYPE, + LOCALES_DIRECTORY, + SYSTEM_LANGUAGE, +} from "jslib-angular/services/jslib-services.module"; +import { BroadcasterService as BroadcasterServiceAbstraction } from "jslib-common/abstractions/broadcaster.service"; +import { CryptoService as CryptoServiceAbstraction } from "jslib-common/abstractions/crypto.service"; +import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "jslib-common/abstractions/cryptoFunction.service"; +import { I18nService as I18nServiceAbstraction } from "jslib-common/abstractions/i18n.service"; +import { + LogService, + LogService as LogServiceAbstraction, +} from "jslib-common/abstractions/log.service"; +import { MessagingService as MessagingServiceAbstraction } from "jslib-common/abstractions/messaging.service"; +import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "jslib-common/abstractions/passwordReprompt.service"; +import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-common/abstractions/platformUtils.service"; +import { StateService as StateServiceAbstraction } from "jslib-common/abstractions/state.service"; +import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service"; +import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service"; +import { SystemService as SystemServiceAbstraction } from "jslib-common/abstractions/system.service"; +import { ClientType } from "jslib-common/enums/clientType"; +import { StateFactory } from "jslib-common/factories/stateFactory"; +import { GlobalState } from "jslib-common/models/domain/globalState"; +import { SystemService } from "jslib-common/services/system.service"; +import { ElectronCryptoService } from "jslib-electron/services/electronCrypto.service"; +import { ElectronLogService } from "jslib-electron/services/electronLog.service"; +import { ElectronPlatformUtilsService } from "jslib-electron/services/electronPlatformUtils.service"; +import { ElectronRendererMessagingService } from "jslib-electron/services/electronRendererMessaging.service"; +import { ElectronRendererSecureStorageService } from "jslib-electron/services/electronRendererSecureStorage.service"; +import { ElectronRendererStorageService } from "jslib-electron/services/electronRendererStorage.service"; + +import { Account } from "../../models/account"; +import { I18nService } from "../../services/i18n.service"; +import { LoginGuardService } from "../../services/loginGuard.service"; +import { NativeMessagingService } from "../../services/nativeMessaging.service"; +import { PasswordRepromptService } from "../../services/passwordReprompt.service"; +import { StateService } from "../../services/state.service"; +import { SearchBarService } from "../layout/search/search-bar.service"; + +import { InitService } from "./init.service"; + +const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); + +@NgModule({ + imports: [JslibServicesModule], + declarations: [], + providers: [ + InitService, + NativeMessagingService, + SearchBarService, + LoginGuardService, + { + provide: APP_INITIALIZER, + useFactory: (initService: InitService) => initService.init(), + deps: [InitService], + multi: true, + }, + { + provide: STATE_FACTORY, + useValue: new StateFactory(GlobalState, Account), + }, + { + provide: CLIENT_TYPE, + useValue: ClientType.Desktop, + }, + { + provide: RELOAD_CALLBACK, + useValue: null, + }, + { provide: LogServiceAbstraction, useClass: ElectronLogService, deps: [] }, + { + provide: PlatformUtilsServiceAbstraction, + useClass: ElectronPlatformUtilsService, + deps: [ + I18nServiceAbstraction, + MessagingServiceAbstraction, + CLIENT_TYPE, + StateServiceAbstraction, + ], + }, + { + provide: I18nServiceAbstraction, + useClass: I18nService, + deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY], + }, + { + provide: MessagingServiceAbstraction, + useClass: ElectronRendererMessagingService, + deps: [BroadcasterServiceAbstraction], + }, + { provide: StorageServiceAbstraction, useClass: ElectronRendererStorageService }, + { provide: SECURE_STORAGE, useClass: ElectronRendererSecureStorageService }, + { + provide: CryptoServiceAbstraction, + useClass: ElectronCryptoService, + deps: [ + CryptoFunctionServiceAbstraction, + PlatformUtilsServiceAbstraction, + LogServiceAbstraction, + StateServiceAbstraction, + ], + }, + { + provide: SystemServiceAbstraction, + useClass: SystemService, + deps: [ + MessagingServiceAbstraction, + PlatformUtilsServiceAbstraction, + RELOAD_CALLBACK, + StateServiceAbstraction, + ], + }, + { provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService }, + { + provide: StateServiceAbstraction, + useClass: StateService, + deps: [ + StorageServiceAbstraction, + SECURE_STORAGE, + LogService, + StateMigrationServiceAbstraction, + STATE_FACTORY, + STATE_SERVICE_USE_CACHE, + ], + }, + ], +}) +export class ServicesModule {} diff --git a/src/app/vault/export.component.html b/src/app/vault/export.component.html index f733a845..26325a02 100644 --- a/src/app/vault/export.component.html +++ b/src/app/vault/export.component.html @@ -21,8 +21,8 @@ - - + +
-
- +
+
-
- +
+
@@ -219,6 +232,7 @@ (change)="savePasswordOptions()" [disabled]="enforcedPasswordPolicyOptions?.useLowercase" [(ngModel)]="passwordOptions.lowercase" + attr.aria-label="{{ 'lowercase' | i18n }}" />
@@ -229,6 +243,7 @@ (change)="savePasswordOptions()" [disabled]="enforcedPasswordPolicyOptions?.useNumbers" [(ngModel)]="passwordOptions.number" + attr.aria-label="{{ 'numbers' | i18n }}" />
@@ -239,6 +254,7 @@ (change)="savePasswordOptions()" [disabled]="enforcedPasswordPolicyOptions?.useSpecial" [(ngModel)]="passwordOptions.special" + attr.aria-label="{{ 'specialCharacters' | i18n }}" />
@@ -295,8 +311,12 @@
-
-