Compare commits

...

136 Commits

Author SHA1 Message Date
Oscar Hinton d7674f5c91
Update README.md (#1531) 2022-05-05 15:06:09 -04:00
Jake Fink 9852f2ec22
[PS-106] update jslib (#1530) 2022-05-05 17:11:39 +02:00
Micaiah Martin fec6838f90
Updated publisher to use generic (#1529) 2022-05-05 09:51:17 -05:00
Patrick H. Lauke 55a9403ee3
[PS-540] Accessibility: remove `appBlurClick`, add `aria-expanded` to cog options, links to buttons, fix unsufficient focus indication (#1514) 2022-05-05 16:36:46 +02:00
Oscar Hinton 817856bc82
Rust improvements (#1495) 2022-05-05 16:01:09 +02:00
Micaiah Martin 508292ae39
Patched build workflow (#1527) 2022-05-04 13:59:36 -05:00
Micaiah Martin 00fd2ec03f
Update publish settings to use S3 (#1521)
* Update publish settings to use S3

* Fix formatting

* Added endpoint with new domain

* Updated S3 publisher config

* added npm CD commands for workflow later.

* Updated release workflow to publish to S3

* testing release

* Reduce aws cli output

* Remove test

* Finalize release workflow
- Reverted back testing logic
- Removed dry run check for GH release since it creates it as a draf anyways
- Removed artifact_url env as it's no longer needed.

* Remove testing values

* Merge Master

* Added endpoint in config
2022-05-04 11:19:04 -04:00
Oscar Hinton 9a954710d9
Add keytar to externals (#1520) 2022-05-03 11:03:21 +02:00
Oscar Hinton a81c3c95a4
[CP-30] Add credit card pipe (#1517) 2022-05-02 19:45:24 +02:00
Kyle Spearrin 75470dc169
Forwarded email providers to username generator (#1511)
* forward email username generation

* update jslib
2022-05-02 10:57:01 -04:00
Oscar Hinton 18b5e4adfd
[EC-163] Undo move to rust implementation (#1509) 2022-05-02 12:39:38 +02:00
Patrick H. Lauke 0396d682b1
Change links to buttons, expose `aria-pressed` for toggles, add `aria-expanded` to send view's "Options" (#1437)
* Change links to buttons, expose `aria-pressed` for toggles

- also make existing `<a routerLink...>` type controls keyboard focusable with the addition of `tabindex="0"`

* Correctly set aria-pressed

now that I have a working build environment, could verify correct way to set this with my limited Angular knowledge

* Change more links to buttons, initial style changes

* Fix layout of <button> elements with .box-content-row

* Update jslib submodule

* Add `aria-expanded` to the send view's "Show options" expand/collapse control

* Fix position of "Edit" pencil when hovering over folders

* Update jslib

* Change sends list links to buttons

* Add `aria-pressed` to vault and send list buttons

Programmatically denote which of the buttons is currently active/shown in the right-most panel

* Fix incorrect "Options" expand/collapse button in add-edit view

Currently, that buttons lacks an accName because the "Options" text is outside of it.

* Add `aria-pressed` to the send left-hand column filters

* Simplify base, list, and vault styles

Since links are now buttons, no need to double up selectors for both types of elements. No need to double-up theming in base, as this also causes incorrect "x" in toasts.

* Remove unnecessary `position:relative`

Fixes issue with cut-off focus outlines, has no other adverse effect

* Fix styling for last child of action buttons

Old approach of making right padding smaller results in unsightly, off-center icon (noticeable when focus outline is visible). This visually remains the same, but reduces right-hand margin instead.
2022-04-30 16:09:41 +02:00
Thomas Rittson ef60112855
[PS-74] Fix user authentication state checks (#1464)
* Use new authStatus method, clean up account switcher

* Update naming

* update jslib
2022-04-30 09:16:46 +10:00
Thomas Rittson b467206448
[EC-156] [BEEEP] Remove factory providers in Angular DI (#1496)
* Use tokens

* Use initService

Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
2022-04-29 17:48:44 +10:00
Nils Fahldieck 55b301c267
Widen the sidebar to 600px max (#1503)
This implements the suggestion by MrBlack of the community forums in: https://community.bitwarden.com/t/manually-resizable-sidebar-columns-in-bitwarden-desktop-app/31909/3
2022-04-27 22:10:11 +02:00
github-actions[bot] a5ebb9fb52
Bumped version to 1.33.1 (#1502)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-04-25 11:40:39 -07:00
github-actions[bot] 2764c9610b
Bumped version to 1.33.0 (#1501)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-04-25 08:02:04 -07:00
Oscar Hinton 241e08b7ff
[EC-166] Move electron metadata to separate file (#1498)
* Move electron metadata to separate file

* Replace yml with json

* Update build script
2022-04-25 15:54:08 +02:00
Oscar Hinton 865e92c94c
Improve reorganization note (#1497) 2022-04-22 17:13:06 +02:00
Patrick H. Lauke c3d0a529fb
[CP-92] Add new more understandable labels to password generator checkboxes (#1461) 2022-04-22 15:26:38 +02:00
Oscar Hinton 1315b3c6cd
Add reorganization notice (#1494) 2022-04-22 09:51:13 +02:00
Dave Nicolson 9f77dd9d09
[CP-96] Capitalize menu items consistently (#1489) 2022-04-21 19:44:53 +02:00
Thomas Rittson 5082c7708a
[PS-211] [PS-212] Make Generator page accessible (#1493)
* Fix radiobutton names

* Add role=radiogroup

* Add aria-labelledby
2022-04-21 09:51:37 -04:00
dwbit 10d35d863b
Contribution Documentation edits (#1486)
Making corrections to the mobile contributions doc:

Update Crowdin contact from Kyle to dwbit.
Update 'User-to-User Support' forum category to 'Ask the Bitwarden Community category'
2022-04-21 08:02:41 -04:00
Kyle Spearrin caafbc2b73
fix disabling of generate type (#1491) 2022-04-20 11:46:12 -04:00
Thomas Rittson a4ca9bf64c
Update jslib (#1490)
* Update jslib

* Update name of UserVerificationComponent
2022-04-19 10:29:43 -05:00
Joseph Flinn 3862a19571
Bumping pinned commit of the download-artifact action to bypass the broken GitHub api (#1488) 2022-04-18 14:28:49 -07:00
Oscar Hinton 8be88a731c
Resolve rust for windows and linux (#1485) 2022-04-13 16:32:08 +02:00
Oscar Hinton 2b0d7ac72c
Bump libsecret for rust (#1480) 2022-04-13 08:04:48 -05:00
Matt Gibson e0d7d2b43a
Add descriptions to vague messages (#1476)
* Add descriptions to vague messages

* Fix typo
2022-04-12 15:36:25 +02:00
Oscar Hinton 70db11e659
Update readme with rust instructions (#1482) 2022-04-12 08:24:09 +02:00
Oscar Hinton 2e3c89269d
Resolve cross-compile for rust (#1481) 2022-04-11 19:53:16 +02:00
github-actions[bot] 31523bdf0e
Autosync the updated translations (#1478)
Co-authored-by: github-actions <>
2022-04-08 11:50:48 +02:00
Oscar Hinton 6b6666cd0d
Undo electron builder bump (#1477) 2022-04-07 22:13:58 +02:00
Oscar Hinton be1ab221f4
Bump electron dependencies (#1447)
Co-authored-by: Micaiah Martin <mmartin@bitwarden.com>
2022-04-05 19:46:07 +02:00
Oscar Hinton 78986023e7
[BEEEP] Add native rust module (#1379) 2022-04-05 16:54:44 +02:00
Addison Beck 14b9decf21
[bug] Adjust mode in render webpack.config to variable (#1473) 2022-04-04 10:45:35 -05:00
github-actions[bot] 0e9465601a
Autosync the updated translations (#1470)
Co-authored-by: github-actions <>
2022-04-01 13:10:34 +02:00
Oscar Hinton 52bb77fb66
Add electron:ignore which ignores invalid certificates (#1463) 2022-04-01 11:24:46 +02:00
Kyle Spearrin b4cec5b46f
generator updates (#1469)
* generator updates

* update jslib
2022-03-31 18:49:53 -04:00
Robyn MacCallum 72405ebe87
update jslib (#1467) 2022-03-31 11:39:27 -04:00
Patrick H. Lauke 4379274154
Add `aria-label` to password generator length slider, make it non-keyboard-focusable (#1459)
Closes https://github.com/bitwarden/desktop/issues/1458

Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
2022-03-31 13:14:16 +10:00
Kyle Spearrin 9e0cc45704
Username generator (#1456)
* username generator implemented

* disable type when coming from add/edit

* restyle buttons to new icon-btn

* update generated-wrapper styles

* only show policy messages for passwords

* make generated-wrapper a standalone style

* Update src/app/vault/password-generator.component.html

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>

* aria-expanded on show options

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2022-03-29 15:01:57 -04:00
Francesco De Feo bc21703a2b
Update macOS icon to follow Big Sur design (#837)
Co-authored-by: Hinton <oscar@oscarhinton.com>
2022-03-29 20:56:03 +02:00
Patrick H. Lauke 4ddeff2eee
Add `aria-pressed` to all eye/toggle visibility buttons (#1443)
* Add `aria-pressed` to all eye/toggle visibility buttons

Closes #1442

* Correct custom field visibility toggle `aria-pressed` value
2022-03-27 22:42:47 +02:00
Patrick H. Lauke 75af5b94d4
Fix "Custom environment", "Options" expand/collapse controls - wrong accessible name, state not exposed (#1441)
* Fix "Custom environment" expand/collapse control wrong accessible name, expose state

Closes #1440

* Correct "Options" expand/collapse accname, expose state

* Remove redundant appA11yTitle
2022-03-27 15:04:45 +02:00
Oscar Hinton cbadcccc85
Fix Mac App Store minimumSystemVersion (#1453) 2022-03-25 15:29:43 +01:00
Kyle Spearrin 21167301f1
Bump jslib and add compatability with username generation (#1450)
Co-authored-by: Hinton <oscar@oscarhinton.com>
2022-03-25 10:56:34 +01:00
github-actions[bot] ad6308eb48
Autosync the updated translations (#1451)
Co-authored-by: github-actions <>
2022-03-25 01:28:44 +01:00
Oscar Hinton fec023866e
Add utils for testing updating processes (#1448) 2022-03-24 21:23:29 +01:00
Jake Fink d2a7012d3f
PS-91 bug/mas hide update option (#1446)
* PS-91 - hide update option if MAS build

* fix isMacAppStore
2022-03-24 15:55:06 -04:00
Jake Fink 6555312034
PS-91 - hide update option if MAS build (#1432) 2022-03-23 13:03:08 -04:00
github-actions[bot] e2fe0c8b09
Bump version to 1.32.2 (#1429)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- Patch for Testflight track.
2022-03-22 13:30:43 -06:00
Oscar Hinton 3b2427e913
Change minimumSystemVersion to only apply for MAS (#1428) 2022-03-22 13:43:37 -04:00
Oscar Hinton 37e9523f00
Define Angular CLI globals to support tree shaking (#1408) 2022-03-22 10:09:19 +01:00
github-actions[bot] 09d7376f39
Bump version to 1.32.1 (#1427)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-03-21 14:09:45 -06:00
Thomas Rittson be0b6113e4
[JslibModule] Refactor to use JslibModule (#1425) 2022-03-21 20:39:35 +10:00
github-actions[bot] 75f11cdbf1
Autosync the updated translations (#1421)
Co-authored-by: github-actions <>
2022-03-18 01:13:12 +01:00
github-actions[bot] e5feda0fa6
Bump version to 1.32.0 (#1415)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-03-16 10:46:16 -06:00
Micaiah Martin 5ebed777c2
Updated actions (#1412) 2022-03-16 10:11:17 -06:00
Vince Grassia e49d317d19
Remove safari build script from Build workflow (#1413) 2022-03-15 16:47:02 -04:00
Chad Scharf 114d7455b8
Update SECURITY.md (#1411)
* Update SECURITY.md

Add link to our HackerOne program for submitting potential security issues.

* Revise language on SECURITY.md
2022-03-15 15:56:26 -04:00
Vince Grassia d1146acb1e
Add Node caching to Build workflow (#1410) 2022-03-15 15:19:12 -04:00
Micaiah Martin 703d390566
Moved linting to it's own step, removed duplicates (#1404) 2022-03-15 10:38:13 -06:00
Thomas Rittson c0170563f2
Disable disk caching in stateService (#1406)
* Disable disk caching in stateService

* Turn back on caching for the renderer

Co-authored-by: addison <addisonbeck1@gmail.com>
2022-03-15 09:25:43 -04:00
Thomas Rittson f46405a508
Update jslib (#1403) 2022-03-15 15:15:49 +10:00
Micaiah Martin 6d429465fb
Added manual build trigger (#1405) 2022-03-14 16:53:59 -06:00
Matt Gibson 81457f6c05
Add jit entitlement (#1395)
* Add arm64 package

* temporarily remove workflow ignore

* Don't delete universal dist when completing arm64

* Add allow-jit entitlement

* Revert "Don't delete universal dist when completing arm64"

This reverts commit b06d638345.

* Revert "temporarily remove workflow ignore"

This reverts commit 8007983547.

* Revert "Add arm64 package"

This reverts commit c8359dae4d.

Co-authored-by: Hinton <oscar@oscarhinton.com>
2022-03-11 10:01:19 -06:00
github-actions[bot] aee27f9570
Autosync the updated translations (#1399)
Co-authored-by: github-actions <>
2022-03-11 02:25:09 +01:00
Joseph Flinn f845bcf6c1
Update hotfix release branch name to hotfix-rc (#1396) 2022-03-09 12:46:13 -08:00
Micaiah Martin b134eba27b
Renewed certs and profiles (#1393) 2022-03-04 10:25:28 -07:00
Vincent Salucci e84be59075
[Captcha] Implement captcha for 2fa (#1390)
* [Captcha] Implement captcha for 2fa

* Update jslib

* Added remaining items necessary for captcha // updated login to match 2fa style
2022-03-03 18:20:29 -06:00
github-actions[bot] edc5245173
Autosync the updated translations (#1394)
Co-authored-by: github-actions <>
2022-03-04 01:14:56 +01:00
Addison Beck 7bad97dd82
[dep] Update jslib (#1392) 2022-03-03 20:34:39 +01:00
Oscar Hinton 3071bec03f
Update NSIS build settings (#1389) 2022-03-03 19:50:35 +01:00
Thomas Rittson 80415f8cd5
Update jslib (#1387) 2022-03-02 08:49:07 +10:00
Daniel James Smith 03c279865f
BEEEP: Add missing languages (#1382)
* Pull in jslib

* Register missing languages
2022-03-01 13:52:53 +01:00
Addison Beck 54025f269a
[feature] Implement scope warning for exports (#1384) 2022-02-28 17:44:10 +01:00
Thomas Rittson 4ce5e5fbdc
Update Help menu options (#1383) 2022-02-28 11:23:14 +01:00
Addison Beck c738366eef
[dep] Update jslib (#1381) 2022-02-25 11:27:43 -05:00
Micaiah Martin 0114d96e18
[BEEEP] - Ignored workflow files from triggering builds (#1375) 2022-02-25 08:14:19 -06:00
Micaiah Martin 1eac8f0c0f
Added dry run logic (#1376) 2022-02-25 08:13:39 -06:00
Daniel James Smith 8880e9700e
BEEEP: Colorize hidden custom field when value visible (#1380) 2022-02-25 14:27:54 +01:00
github-actions[bot] 2f289ebd1f
Autosync the updated translations (#1378)
Co-authored-by: github-actions <>
2022-02-25 12:29:23 +01:00
Oscar Hinton 3aa52a5537
Add eslint (#1369) 2022-02-24 20:50:19 +01:00
Robyn MacCallum 233f876bbb
Accessibility fixes for account switcher menu (#1373) 2022-02-24 14:11:54 -05:00
Thomas Rittson d7276850a2
Update jslib (#1374)
* Update jslib

* Update i18n
2022-02-24 09:36:15 -05:00
Jake Fink e570551a5a
fix desktop close window shortcut (#1372)
* fix syntax not adding additional items

* lint
2022-02-23 09:58:59 -05:00
Vincent Salucci 7401204b70
[Icons] Added missing custom field sort icon (#1370) 2022-02-23 07:54:33 -06:00
github-actions[bot] 685ffbcac5
Bumped version to 1.31.4 (#1362)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-22 05:51:46 -08:00
Chad Scharf f9e6d13708
We're Hiring (#1367)
Added link to README.md for Bitwarden Careers page.
2022-02-22 14:04:32 +01:00
Micaiah Martin f798995764
Added linter workflow (#1355) 2022-02-18 13:28:17 -06:00
github-actions[bot] f6298b2684
Autosync the updated translations (#1360)
Co-authored-by: github-actions <>
2022-02-18 10:21:18 +01:00
Thomas Rittson 3881223a73
Exclude jslib from prettier hook (#1343)
* Exlude jslib from prettier hook
2022-02-17 10:37:41 +10:00
Matt Gibson d2df8dacad
Add label enforcement (#1346)
* Enforce Hold label

* Linting

Co-authored-by: Micaiah Martin <77340197+mimartin12@users.noreply.github.com>
2022-02-16 08:43:26 -06:00
Addison Beck 10ffdce5b9
[lib] Update jslib (#1347) 2022-02-15 15:05:31 -05:00
github-actions[bot] d6d74e178c
Bumped version to 1.31.3 (#1345)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-15 09:04:33 -05:00
Addison Beck 2e09265d3a
[bug] Fix tab order regression (#1340) 2022-02-14 15:43:05 -05:00
Thomas Rittson 659c9ea78d
Update i18n string and id for tray menu Lock item (#1334) 2022-02-14 08:40:06 -05:00
Addison Beck 884615c23a
update jslib (#1337) 2022-02-14 08:32:55 -05:00
Oscar Hinton 2470d8ce25
Resolve preferences not setting correct focus (#1336) 2022-02-14 14:23:05 +01:00
github-actions[bot] 6b6468e061
Bumped version to 1.31.2 (#1328)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-11 14:21:09 -08:00
github-actions[bot] 45b144361c
Autosync the updated translations (#1313)
Co-authored-by: github-actions <>
2022-02-11 22:46:20 +01:00
Addison Beck 881bb3cb49
Update jslib (#1327)
* Update jslib

* Add a null check

* Reworked condition

* Ran prettier
2022-02-11 15:34:36 -05:00
Addison Beck fd4c41b043
update jslib (#1324)
* update jslib

* Update jslib
2022-02-11 12:51:48 -05:00
Oscar Hinton 7a8a78b4df
Bump jslib (https://github.com/bitwarden/jslib/pull/673) (#1322) 2022-02-11 16:31:13 +01:00
Oscar Hinton f82e2fbb03
Bump jslib (performance and hasKeyStored fix) (#1321) 2022-02-11 14:53:49 +01:00
Matt Gibson e9a3c586f8
Fix/load lock component (#1319)
* Update jslib

* update jslib
2022-02-11 03:58:31 -06:00
Thomas Rittson f54a614d6b
Update jslib (#1318) 2022-02-11 00:30:53 -05:00
Thomas Rittson 95b5d68566
Update jslib (#1317) 2022-02-11 15:02:04 +10:00
Thomas Rittson 3ffb658db6
Update jslib (#1316) 2022-02-11 14:29:47 +10:00
github-actions[bot] 530f25c88a
Bump version to 1.31.1 (#1308)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-10 13:47:25 -07:00
Joseph Flinn 97d367dab8
Switching where we are pulling our snap token from (#1307) 2022-02-10 10:57:32 -08:00
Micaiah Martin 0a545c88b2
Update release.yml (#1306) 2022-02-10 10:51:50 -07:00
github-actions[bot] 0f6ee08dd5
Bump version to 1.31.0 (#1304)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-10 09:40:21 -07:00
Addison Beck b227ae13f6
Remove unused import (#1298) 2022-02-09 21:34:01 -05:00
Addison Beck 79f6a33596
[bug] Remove redundant token clean call (#1303)
* [bug] Remove redundant token clean call

* bump jslib
2022-02-09 17:06:30 -05:00
Addison Beck 1e80c4335f
[bug] Resolve several regression issues (#1302)
* [bug] Ensure accounts logging out in the background doesn't impact active account ui

The main issue here: inactive accounts with a logout timeout actually log out the active account" is fixed by pulling in jslib.
These changes are for some asthetic issues I noticed, where inactive accounts logging out still fires a switchAccount event, which causes a loading spinner to appear and a sync that redraws the vault.

* Only load if the account being logged out is the active account:
* Replaced any calls to `stateService.activeAccount.getValue` with references to `this.activeUserId`, since we subscribe to that in the component now.
* Only send a "switchAccount" method if the active user before a clean and after a clean don't match

* [bug] Ensure default vault timeout is set to On Restart

We dont override the StateMigrationService instance that is injected in desktop, so it is not aware of desktop defaults.
This results in fresh accounts having a "Never" timeout action insteads of "On Restart"

* Use the correct StateMigrationService instance

* update jslib
2022-02-09 12:42:16 -05:00
Vincent Salucci c51b8523b7
[Help] Update links to new pattern (#1300)
* [Help] Update links to new pattern

* Update jslib
2022-02-08 18:02:17 -06:00
Joseph Flinn bd2ed43498
Fixing safari ref logic (#1299) 2022-02-08 07:41:58 -08:00
Oscar Hinton 243afc9da0
Client & Version headers (#1297) 2022-02-08 15:29:01 +01:00
Thomas Rittson 762e3f8198
Update client for authService refactor (#1239) 2022-02-08 09:26:53 +10:00
Thomas Rittson 4e75a25492
Make husky pre-commit hook executable (#1295) 2022-02-08 08:33:25 +10:00
Addison Beck 143a262743
[chore] Update jslib (#1296) 2022-02-07 12:08:15 -05:00
Addison Beck c1a3178538
Update jslib (#1293)
* Update jslib

* [style] Ran prettier
2022-02-03 14:39:49 -05:00
Oscar Hinton 166c459da4
Potentially improve path detection of regedit vbs files (#1288) 2022-02-03 19:39:23 +01:00
Daniel James Smith 94b561382d
fix announcement of security header (#1292) 2022-02-03 17:58:55 +01:00
Daniel James Smith 2b58861296
[AccountSwitching]Make account switcher accessible (#1289)
* Make account switcher keyboard accessible

* ScreenReader: Announce submenu and expansion

* ScreenReader: Announc switch account button with account info

* Fix tab focus on dropdown

* Fix esc not changing state

* Fix linting issues

Co-authored-by: Hinton <oscar@oscarhinton.com>
2022-02-03 17:46:14 +01:00
Vincent Salucci 3e8705d548
[Icons] FF - requested icon changes (#1291)
* [Icons] Remove FA

* Icon changes // webpack correction
2022-02-03 10:28:34 -06:00
Oscar Hinton 1e877f6cf8
Set minimumSystemVersion (#1287) 2022-02-03 14:13:50 +01:00
Joseph Flinn 9151fc0164
Switching the way we are pulling secrets for the EV SSL cert (#1285) 2022-02-02 14:35:03 -08:00
Addison Beck 03eed41d86
[bug] Remove scroll from login screen (#1283) 2022-02-02 10:32:06 -05:00
Addison Beck cad6e9481f
[bug] Attempt to resolve windows portable build issues (#1280) 2022-02-02 10:28:36 -05:00
Daniel James Smith 270411dff7
Fix icons on the settings headers (#1279) 2022-02-02 15:50:48 +01:00
Vincent Salucci 7c68e3802e
[bug] Add options string (#1281) 2022-02-01 23:06:29 -06:00
Addison Beck 48ff9f61ae
[bug] Inactive accounts with power-based timeout settings are not timing out (#1278)
* [bug] Move bulk of system lock checks into app.component

* [review] Extract shared system timeout logic

* [review] Correct an improper number

* [review] Opt for a more locally scoped system timeout helper than a dedicated enum
2022-02-01 11:31:30 -05:00
229 changed files with 15297 additions and 4570 deletions

9
.eslintignore Normal file
View File

@ -0,0 +1,9 @@
dist
build
jslib
webpack.main.js
webpack.renderer.js
src/scripts/duo.js
desktop_native
**/node_modules

32
.eslintrc.json Normal file
View File

@ -0,0 +1,32 @@
{
"root": true,
"env": {
"browser": true,
"node": true
},
"extends": ["./jslib/shared/eslintrc.json"],
"rules": {
"import/order": [
"error",
{
"alphabetize": {
"order": "asc"
},
"newlines-between": "always",
"pathGroups": [
{
"pattern": "jslib-*/**",
"group": "external",
"position": "after"
},
{
"pattern": "src/**/*",
"group": "parent",
"position": "before"
}
],
"pathGroupsExcludedImportTypes": ["builtin"]
}
]
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,6 +6,10 @@ on:
branches-ignore:
- 'l10n_master'
- 'gh-pages'
paths-ignore:
- '.github/workflows/**'
workflow_dispatch:
inputs: {}
jobs:
cloc:
@ -13,7 +17,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up cloc
run: |
@ -35,7 +39,7 @@ jobs:
hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Get Package Version
id: retrieve-version
@ -55,9 +59,9 @@ jobs:
run: |
SAFARI_REF=master
if [ "$GITHUB_REF" = "hotfix" ]; then
SAFARI_REF=hotfix
elif [ "$GITHUB_REF" = "rc" ]; then
if [[ "$GITHUB_REF" == "refs/heads/hotfix-rc" ]]; then
SAFARI_REF=hotfix-rc
elif [[ "$GITHUB_REF" == "refs/heads/rc" ]]; then
SAFARI_REF=rc
fi
@ -73,12 +77,34 @@ jobs:
echo "::set-output name=rc_branch_exists::0"
fi
if [[ $(git ls-remote --heads origin hotfix) ]]; then
if [[ $(git ls-remote --heads origin hotfix-rc) ]]; then
echo "::set-output name=hotfix_branch_exists::1"
else
echo "::set-output name=hotfix_branch_exists::0"
fi
lint:
name: Lint
runs-on: ubuntu-20.04
steps:
- name: Checkout repo
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Cache npm
id: npm-cache
uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
with:
path: '~/.npm'
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install Node dependencies
run: npm ci
- name: Run linter
run: npm run lint
linux:
name: Linux Build
@ -88,20 +114,15 @@ jobs:
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.1.5
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: '16'
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
with:
path: '~/.npm'
key: ${{ runner.os }}-${{ github.run_id }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Set Node options
run: echo "NODE_OPTIONS=--max_old_space_size=4096" >> $GITHUB_ENV
@ -128,49 +149,60 @@ jobs:
- name: Install Node dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Cache Native Module
uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2
id: cache
with:
path: desktop_native/*.node
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
- name: Upload .deb artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb
if-no-files-found: error
- name: Upload .rpm artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm
if-no-files-found: error
- name: Upload .freebsd artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd
if-no-files-found: error
- name: Upload .snap artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap
path: ./dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap
if-no-files-found: error
- name: Upload .AppImage artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage
if-no-files-found: error
- name: Upload latest auto-update artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: latest-linux.yml
path: ./dist/latest-linux.yml
@ -185,20 +217,15 @@ jobs:
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.1.5
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: '16'
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
with:
path: '%AppData%/npm-cache'
key: ${{ runner.os }}-${{ github.run_id }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Set Node options
run: echo "NODE_OPTIONS=--max_old_space_size=4096" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
shell: pwsh
@ -215,26 +242,60 @@ 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
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
with:
keyvault: "bitwarden-prod-kv"
secrets: "code-signing-vault-url,
code-signing-client-id,
code-signing-tenant-id,
code-signing-client-secret,
code-signing-cert-name"
- name: Install Node dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Cache Native Module
uses: actions/cache@48af2dc4a9e8278b89d7fa154b955c30c6aaab09 # v3.0.2
id: cache
with:
path: desktop_native/*.node
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
SIGNING_VAULT_URL: ${{ secrets.SIGNING_VAULT_URL }}
SIGNING_CLIENT_ID: ${{ secrets.SIGNING_CLIENT_ID }}
SIGNING_TENANT_ID: ${{ secrets.SIGNING_TENANT_ID }}
SIGNING_CLIENT_SECRET: ${{ secrets.SIGNING_CLIENT_SECRET }}
SIGNING_CERT_NAME: ${{ secrets.SIGNING_CERT_NAME }}
SIGNING_VAULT_URL: ${{ steps.retrieve-secrets.outputs.code-signing-vault-url }}
SIGNING_CLIENT_ID: ${{ steps.retrieve-secrets.outputs.code-signing-client-id }}
SIGNING_TENANT_ID: ${{ steps.retrieve-secrets.outputs.code-signing-tenant-id }}
SIGNING_CLIENT_SECRET: ${{ steps.retrieve-secrets.outputs.code-signing-client-secret }}
SIGNING_CERT_NAME: ${{ steps.retrieve-secrets.outputs.code-signing-cert-name }}
run: |
npm run build
npm run pack:win
@ -262,91 +323,91 @@ jobs:
choco pack ./dist/chocolatey/bitwarden.nuspec --version "$env:_PACKAGE_VERSION" --out ./dist/chocolatey
- name: Upload portable exe artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe
path: ./dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe
if-no-files-found: error
- name: Upload installer exe artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe
path: ./dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe
if-no-files-found: error
- name: Upload appx ia32 artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx
if-no-files-found: error
- name: Upload store appx ia32 artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx
if-no-files-found: error
- name: Upload NSIS ia32 artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z
path: ./dist/nsis-web/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z
if-no-files-found: error
- name: Upload appx x64 artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx
if-no-files-found: error
- name: Upload store appx x64 artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx
if-no-files-found: error
- name: Upload NSIS x64 artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z
path: ./dist/nsis-web/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z
if-no-files-found: error
- name: Upload appx ARM64 artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx
if-no-files-found: error
- name: Upload store appx ARM64 artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx
if-no-files-found: error
- name: Upload NSIS ARM64 artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z
path: ./dist/nsis-web/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z
if-no-files-found: error
- name: Upload nupkg artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg
path: ./dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg
if-no-files-found: error
- name: Upload latest auto-update artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: latest.yml
path: ./dist/nsis-web/latest.yml
@ -361,20 +422,15 @@ jobs:
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.1.5
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: '16'
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
with:
path: '~/.npm'
key: ${{ runner.os }}-${{ github.run_id }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Set Node options
run: echo "NODE_OPTIONS=--max_old_space_size=4096" >> $GITHUB_ENV
@ -383,23 +439,28 @@ 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"
- name: Cache Build
id: build-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
with:
path: build
key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Cache Safari
id: safari-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
with:
path: dist-safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@ -471,16 +532,27 @@ 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/*.node
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
- name: Run linter
run: npm run lint
- name: Build application (dev)
run: npm run build
@ -489,39 +561,39 @@ jobs:
run: New-Item ./dist-safari -ItemType Directory -ea 0
- name: Checkout browser extension
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
with:
repository: 'bitwarden/browser'
path: 'dist-safari/browser'
path: 'dist-safari/bitwarden'
ref: ${{ needs.setup.outputs.safari_ref }}
- name: Build Safari extension
shell: pwsh
run: ./scripts/safari-build.ps1 -skipcheckout -skipoutcopy
run: |
npm install
npm run dist:safari
working-directory: dist-safari/bitwarden/apps/browser
macos-package-github:
name: MacOS Package GitHub Release Assets
runs-on: macos-11
needs: [setup, macos-build]
needs:
- setup
- macos-build
- lint
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.1.5
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: '16'
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
with:
path: '~/.npm'
key: ${{ runner.os }}-${{ github.run_id }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Set Node options
run: echo "NODE_OPTIONS=--max_old_space_size=4096" >> $GITHUB_ENV
@ -530,23 +602,28 @@ 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"
- name: Get Build Cache
id: build-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
with:
path: build
key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Setup Safari Cache
id: safari-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
with:
path: dist-safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@ -618,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/*.node
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
@ -636,20 +727,24 @@ jobs:
- name: Checkout browser extension
if: steps.safari-cache.outputs.cache-hit != 'true'
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
with:
repository: 'bitwarden/browser'
path: 'dist-safari/browser'
repository: 'bitwarden/bitwarden'
path: 'dist-safari/bitwarden'
ref: ${{ needs.setup.outputs.safari_ref }}
- name: Build Safari extension
if: steps.safari-cache.outputs.cache-hit != 'true'
shell: pwsh
run: ./scripts/safari-build.ps1 -skipcheckout -skipoutcopy
run: |
npm install
npm run dist:safari
working-directory: dist-safari/bitwarden/apps/browser
- name: Load Safari extension for .dmg
shell: pwsh
run: ./scripts/safari-build.ps1 -copyonly
working-directory: dist-safari/bitwarden/apps/browser
run: |
mkdir PlugIns
cp -r dist/Safari/dmg/build/Release/safari.appex PlugIns/safari.appex
- name: Build application (dist)
env:
@ -658,28 +753,28 @@ jobs:
run: npm run pack:mac
- name: Upload .zip artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip
if-no-files-found: error
- name: Upload .dmg artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg
if-no-files-found: error
- name: Upload .dmg blockmap artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap
path: ./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap
if-no-files-found: error
- name: Upload latest auto-update artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: latest-mac.yml
path: ./dist/latest-mac.yml
@ -689,25 +784,23 @@ jobs:
macos-package-mas:
name: MacOS Package Prod Release Asset
runs-on: macos-11
needs: [setup, macos-build]
needs:
- setup
- macos-build
- lint
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.1.5
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: '16'
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
with:
path: '~/.npm'
key: ${{ runner.os }}-${{ github.run_id }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Set Node options
run: echo "NODE_OPTIONS=--max_old_space_size=4096" >> $GITHUB_ENV
@ -716,23 +809,28 @@ 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"
- name: Get Build Cache
id: build-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
with:
path: build
key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Setup Safari Cache
id: safari-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
with:
path: dist-safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@ -804,9 +902,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/*.node
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
@ -822,20 +934,24 @@ jobs:
- name: Checkout browser extension
if: steps.safari-cache.outputs.cache-hit != 'true'
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
with:
repository: 'bitwarden/browser'
path: 'dist-safari/browser'
path: 'dist-safari/bitwarden'
ref: ${{ needs.setup.outputs.safari_ref }}
- name: Build Safari extension
if: steps.safari-cache.outputs.cache-hit != 'true'
shell: pwsh
run: ./scripts/safari-build.ps1 -skipcheckout -skipoutcopy
run: |
npm install
npm run dist:safari
working-directory: dist-safari/bitwarden/apps/browser
- name: Load Safari extension for App Store
shell: pwsh
run: ./scripts/safari-build.ps1 -mas -copyonly
working-directory: dist-safari/bitwarden/apps/browser
run: |
mkdir PlugIns
cp -r dist/Safari/mas/build/Release/safari.appex PlugIns/safari.appex
- name: Build application for App Store
run: npm run pack:mac:mas
@ -844,7 +960,7 @@ jobs:
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
- name: Upload .pkg artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
path: ./dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
@ -859,7 +975,7 @@ jobs:
&& needs.setup.outputs.rc_branch_exists == 0
&& needs.setup.outputs.hotfix_branch_exists == 0)
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|| github.ref == 'refs/heads/hotfix'
|| github.ref == 'refs/heads/hotfix-rc'
run: npm run upload:mas
@ -867,25 +983,23 @@ jobs:
name: MacOS Package Dev Release Asset
if: false # We need to look into how code signing works for dev
runs-on: macos-11
needs: [setup, macos-build]
needs:
- setup
- macos-build
- lint
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.1.5
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: '16'
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
with:
path: '~/.npm'
key: ${{ runner.os }}-${{ github.run_id }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Set Node options
run: echo "NODE_OPTIONS=--max_old_space_size=4096" >> $GITHUB_ENV
@ -903,14 +1017,14 @@ jobs:
- name: Get Build Cache
id: build-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
with:
path: build
key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Setup Safari Cache
id: safari-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed
with:
path: dist-safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@ -982,9 +1096,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/*.node
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
@ -1000,20 +1128,24 @@ jobs:
- name: Checkout browser extension
if: steps.safari-cache.outputs.cache-hit != 'true'
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
with:
repository: 'bitwarden/browser'
path: 'dist-safari/browser'
path: 'dist-safari/bitwarden'
ref: ${{ needs.setup.outputs.safari_ref }}
- name: Build Safari extension
if: steps.safari-cache.outputs.cache-hit != 'true'
shell: pwsh
run: ./scripts/safari-build.ps1 -skipcheckout -skipoutcopy
run: |
npm install
npm run dist:safari
working-directory: dist-safari/bitwarden/apps/browser
- name: Load Safari extension for App Store
shell: pwsh
run: ./scripts/safari-build.ps1 -masdev -copyonly
working-directory: dist-safari/bitwarden/apps/browser
run: |
mkdir PlugIns
cp -r dist/Safari/masdev/build/Release/safari.appex PlugIns/safari.appex
- name: Build dev application for App Store
run: npm run pack:mac:masdev
@ -1026,7 +1158,7 @@ jobs:
run: zip -r Bitwarden-${{ env.PACKAGE_VERSION }}-masdev-universal.zip Bitwarden.app
- name: Upload masdev artifact
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700 # v2.2.3
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
with:
name: Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip
path: ./dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip
@ -1046,22 +1178,22 @@ jobs:
_CROWDIN_PROJECT_ID: "299360"
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
- name: Login to Azure
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
with:
keyvault: "bitwarden-prod-kv"
secrets: "crowdin-api-token"
- name: Upload Sources
uses: crowdin/github-action@e39093fd75daae7859c68eded4b43d42ec78d8ea # v1.3.2
uses: crowdin/github-action@9237b4cb361788dfce63feb2e2f15c09e2fe7415
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
@ -1079,6 +1211,7 @@ jobs:
needs:
- cloc
- setup
- lint
- linux
- windows
- macos-build
@ -1091,6 +1224,7 @@ jobs:
env:
CLOC_STATUS: ${{ needs.cloc.result }}
SETUP_STATUS: ${{ needs.setup.result }}
LINT_STATUS: ${{ needs.lint.result }}
LINUX_STATUS: ${{ needs.linux.result }}
WINDOWS_STATUS: ${{ needs.windows.result }}
MACOS_BUILD_STATUS: ${{ needs.macos-build.result }}
@ -1102,6 +1236,8 @@ jobs:
exit 1
elif [ "$SETUP_STATUS" = "failure" ]; then
exit 1
elif [ "$LINT_STATUS" = "failure" ]; then
exit 1
elif [ "$LINUX_STATUS" = "failure" ]; then
exit 1
elif [ "$WINDOWS_STATUS" = "failure" ]; then
@ -1117,21 +1253,21 @@ jobs:
fi
- name: Login to Azure - Prod Subscription
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
if: failure()
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
if: failure()
with:
keyvault: "bitwarden-prod-kv"
secrets: "devops-alerts-slack-webhook-url"
- name: Notify Slack on failure
uses: act10ns/slack@e4e71685b9b239384b0f676a63c32367f59c2522 # v1.2.2
uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}

16
.github/workflows/enforce-labels.yml vendored Normal file
View File

@ -0,0 +1,16 @@
---
name: Enforce PR labels
on:
pull_request:
types: [labeled, unlabeled, opened, edited, synchronize]
jobs:
enforce-label:
name: EnforceLabel
runs-on: ubuntu-20.04
steps:
- name: Enforce Label
uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
with:
BANNED_LABELS: "hold"
BANNED_LABELS_DESCRIPTION: "PRs on hold cannot be merged"

View File

@ -12,6 +12,7 @@ on:
options:
- Initial Release
- Redeploy
- Dry Run
jobs:
setup:
@ -22,10 +23,11 @@ jobs:
branch-name: ${{ steps.branch.outputs.branch-name }}
steps:
- name: Branch check
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
run: |
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix" ]]; then
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
echo "==================================="
echo "[!] Can only release from the 'rc' or 'hotfix' branches"
echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches"
echo "==================================="
exit 1
fi
@ -60,45 +62,69 @@ jobs:
BRANCH_NAME=$(basename ${{ github.ref }})
echo "::set-output name=branch-name::$BRANCH_NAME"
- name: Login to Azure
uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
with:
keyvault: "bitwarden-prod-kv"
secrets: "aws-electron-access-id, aws-electron-access-key"
- 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
branch: ${{ steps.branch.outputs.branch-name }}
path: ./artifacts
- name: Rename .pkg to .pkg.archive
env:
PKG_VERSION: ${{ steps.retrieve-version.outputs.package_version }}
working-directory: ./artifacts
run: mv Bitwarden-${{ env.PKG_VERSION }}-universal.pkg Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive
- name: Publish artifacts to S3
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }}
AWS_DEFAULT_REGION: 'us-west-2'
run: |
aws s3 cp ./artifacts s3://public-s3-electron-artifacts/desktop/ \
--acl "public-read" \
--recursive \
--quiet
- name: Create release
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 # v2.8.5
env:
PKG_VERSION: ${{ steps.retrieve-version.outputs.package_version }}
with:
artifacts: "Bitwarden-${{ env.PKG_VERSION }}-amd64.deb,
Bitwarden-${{ env.PKG_VERSION }}-x86_64.rpm,
Bitwarden-${{ env.PKG_VERSION }}-x64.freebsd,
bitwarden_${{ env.PKG_VERSION }}_amd64.snap,
Bitwarden-${{ env.PKG_VERSION }}-x86_64.AppImage,
latest-linux.yml,
Bitwarden-Portable-${{ env.PKG_VERSION }}.exe,
Bitwarden-Installer-${{ env.PKG_VERSION }}.exe,
Bitwarden-${{ env.PKG_VERSION }}-ia32-store.appx,
Bitwarden-${{ env.PKG_VERSION }}-ia32.appx,
Bitwarden-${{ env.PKG_VERSION }}-ia32.nsis.7z,
Bitwarden-${{ env.PKG_VERSION }}-x64-store.appx,
Bitwarden-${{ env.PKG_VERSION }}-x64.appx,
Bitwarden-${{ env.PKG_VERSION }}-x64.nsis.7z,
Bitwarden-${{ env.PKG_VERSION }}-arm64-store.appx,
Bitwarden-${{ env.PKG_VERSION }}-arm64.appx,
Bitwarden-${{ env.PKG_VERSION }}-arm64.nsis.7z,
bitwarden.${{ env.PKG_VERSION }}.nupkg,
latest.yml,
Bitwarden-${{ env.PKG_VERSION }}-universal-mac.zip,
Bitwarden-${{ env.PKG_VERSION }}-universal.dmg,
Bitwarden-${{ env.PKG_VERSION }}-universal.dmg.blockmap,
latest-mac.yml,
Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive"
artifacts: "artifacts/Bitwarden-${{ env.PKG_VERSION }}-amd64.deb,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x86_64.rpm,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64.freebsd,
artifacts/bitwarden_${{ env.PKG_VERSION }}_amd64.snap,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x86_64.AppImage,
artifacts/Bitwarden-Portable-${{ env.PKG_VERSION }}.exe,
artifacts/Bitwarden-Installer-${{ env.PKG_VERSION }}.exe,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-ia32-store.appx,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-ia32.appx,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-ia32.nsis.7z,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64-store.appx,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64.appx,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64.nsis.7z,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-arm64-store.appx,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-arm64.appx,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-arm64.nsis.7z,
artifacts/bitwarden.${{ env.PKG_VERSION }}.nupkg,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-universal-mac.zip,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-universal.dmg,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-universal.dmg.blockmap,
artifacts/Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive"
commit: ${{ github.sha }}
tag: v${{ env.PKG_VERSION }}
name: Version ${{ env.PKG_VERSION }}
@ -116,16 +142,28 @@ jobs:
- name: Checkout Repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
- name: Login to Azure
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Retrieve secrets
id: retrieve-secrets
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
with:
keyvault: "bitwarden-prod-kv"
secrets: "snapcraft-store-token"
- name: Install Snap
uses: samuelmeuli/action-snapcraft@10d7d0a84d9d86098b19f872257df314b0bd8e2d # v1.2.0
with:
snapcraft_token: ${{ secrets.SNAP_TOKEN }}
snapcraft_token: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }}
- name: Setup
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
@ -133,10 +171,8 @@ jobs:
artifacts: bitwarden_${{ env._PKG_VERSION }}_amd64.snap
path: ./dist
- name: Test
run: ls -alht dist
- name: Deploy to Snap Store
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
run: |
snapcraft upload dist/bitwarden_${{ env._PKG_VERSION }}_amd64.snap --release stable
snapcraft logout
@ -161,7 +197,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
@ -170,6 +206,7 @@ jobs:
path: ./dist
- name: Push to Chocolatey
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
shell: pwsh
run: |
cd dist

11
.github/workflows/workflow-linter.yml vendored Normal file
View File

@ -0,0 +1,11 @@
---
name: Workflow Linter
on:
pull_request:
paths:
- .github/workflows/**
jobs:
call-workflow:
uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master

0
.husky/pre-commit Normal file → Executable file
View File

View File

@ -2,6 +2,7 @@
build
dist
dist-safari
desktop_native
jslib

View File

@ -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/

View File

@ -2,6 +2,10 @@
[![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)
> **Archived**
>
> This repository is archived, please go to https://github.com/bitwarden/clients for future development.
# Bitwarden Desktop Application
[![Platforms](https://imgur.com/SLv9paA.png "Windows, macOS, and Linux")](https://bitwarden.com/download/)
@ -12,20 +16,23 @@ 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
- Windows users: To compile the native node modules used in the app you will need the _Visual C++ toolset_, available through the standard Visual Studio installer. You will also need to install the _Microsoft Build Tools 2015_ and _Windows 10 SDK 17134_ as additional dependencies in the Visual Studio installer.
- Windows:
- To compile the native node modules used in the app you will need the _Visual C++ toolset_, available through the standard Visual Studio installer. You will also need to install the _Microsoft Build Tools 2015_ and _Windows 10 SDK 17134_ as additional dependencies in the Visual Studio installer.
- Linux:
- The following packages `build-essential libsecret-1-dev libglib2.0-dev`
**Run the app**
## Run the app
```bash
npm install
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:<platform>`, start the dist version and enable desktop integration. This will write some manifests
@ -37,6 +44,10 @@ needs to be added to the `allowed_extensions` section of the manifest. These IDs
It will then be possible to run the desktop application as usual using `npm run electron` and communicate with the browser.
# We're Hiring!
Interested in contributing in a big way? Consider joining our team! We're hiring for many positions. Please take a look at our [Careers page](https://bitwarden.com/careers/) to see what opportunities are currently open as well as what it's like to work at Bitwarden.
# Contribute
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.

View File

@ -1,39 +1,11 @@
Bitwarden believes that working with security researchers across the globe is crucial to keeping our
users safe. If you believe you've found a security issue in our product or service, we encourage you to
notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
Bitwarden believes that working with security researchers across the globe is crucial to keeping our users safe. If you believe you've found a security issue in our product or service, we encourage you to please submit a report through our [HackerOne Program](https://hackerone.com/bitwarden/). We welcome working with you to resolve the issue promptly. Thanks in advance!
# Disclosure Policy
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
effort to quickly resolve the issue.
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
third-party. We may publicly disclose the issue before resolving it, if appropriate.
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
degradation of our service. Only interact with accounts you own or with explicit permission of the
account holder.
- If you would like to encrypt your report, please use the PGP key with long ID
`0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
# In-scope
- Security issues in any current release of Bitwarden. This includes the web vault, browser extension,
and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
code is available at https://github.com/bitwarden.
# Exclusions
The following bug classes are out-of scope:
- Bugs that are already reported on any of Bitwarden's issue trackers (https://github.com/bitwarden),
or that we already know of. Note that some of our issue tracking is private.
- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
upstream maintainer.
- Attacks requiring physical access to a user's device.
- Self-XSS
- Issues related to software or protocols not under Bitwarden's control
- Vulnerabilities in outdated versions of Bitwarden
- Missing security best practices that do not directly lead to a vulnerability
- Issues that do not have any impact on the general public
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue.
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. We may publicly disclose the issue before resolving it, if appropriate.
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our service. Only interact with accounts you own or with explicit permission of the account holder.
- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
While researching, we'd like to ask you to refrain from:
@ -42,4 +14,8 @@ While researching, we'd like to ask you to refrain from:
- Social engineering (including phishing) of Bitwarden staff or contractors
- Any physical attempts against Bitwarden property or data centers
# We want to help you!
If you have something that you feel is close to exploitation, or if you'd like some information regarding the internal API, or generally have any questions regarding the app that would help in your efforts, please email us at https://bitwarden.com/contact and ask for that information. As stated above, Bitwarden wants to help you find issues, and is more than willing to help.
Thank you for helping keep Bitwarden and our users safe!

6
desktop_native/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
target
index.node
**/node_modules
**/.DS_Store
npm-debug.log*
*.node

946
desktop_native/Cargo.lock generated Normal file
View File

@ -0,0 +1,946 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-expr"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e068cb2806bbc15b439846dc16c5f89f8599f2c3e4d73d4449d38f9b2f0b6c5"
dependencies = [
"smallvec",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "convert_case"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8"
[[package]]
name = "core-foundation"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "ctor"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "cxx"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce2295fe8865279f404147e9b2328e5af0ad11a2c016e58c13acfd48a07d8a55"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0aaaa055d4908326f1b4524b23ae53758019b806c0c4f382ea240982e9766b26"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a670224c6686471df12560a0b97a08145082e70bd38e2b0b5383b79e46c3da7"
[[package]]
name = "cxxbridge-macro"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b700096ca0dece28d9535fdb17ab784a8ae155d7f29d39c273643e6292c9620"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "desktop_native"
version = "0.0.0"
dependencies = [
"anyhow",
"core-foundation",
"gio",
"keytar",
"libsecret",
"napi",
"napi-build",
"napi-derive",
"scopeguard",
"security-framework",
"security-framework-sys",
"tokio",
"widestring",
"windows 0.32.0",
]
[[package]]
name = "futures-channel"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-executor"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
[[package]]
name = "futures-task"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
[[package]]
name = "futures-util"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "gio"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96efd8a1c00d890f6b45671916e165b5e43ccec61957d443aff6d7e44f62d348"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"futures-io",
"gio-sys",
"glib",
"libc",
"once_cell",
"thiserror",
]
[[package]]
name = "gio-sys"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d0fa5052773f5a56b8ae47dab09d040f5d9ce1311f4f99006e16e9a08269296"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"winapi",
]
[[package]]
name = "glib"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa570813c504bdf7539a9400180c2dd4b789a819556fb86da7226d7d1b037b49"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"glib-macros",
"glib-sys",
"gobject-sys",
"libc",
"once_cell",
"smallvec",
"thiserror",
]
[[package]]
name = "glib-macros"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41bfd8d227dead0829ac142454e97531b93f576d0805d779c42bfd799c65c572"
dependencies = [
"anyhow",
"heck",
"proc-macro-crate",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "glib-sys"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4366377bd56697de8aaee24e673c575d2694d72e7756324ded2b0428829a7b8"
dependencies = [
"libc",
"system-deps",
]
[[package]]
name = "gobject-sys"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df6859463843c20cf3837e3a9069b6ab2051aeeadf4c899d33344f4aea83189a"
dependencies = [
"glib-sys",
"libc",
"system-deps",
]
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "keytar"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d361c55fba09829ac620b040f5425bf239b1030c3d6820a84acac8da867dca4d"
dependencies = [
"keytar-sys",
]
[[package]]
name = "keytar-sys"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe908c6896705a1cb516cd6a5d956c63f08d95ace81b93253a98cd93e1e6a65a"
dependencies = [
"cc",
"cxx",
"cxx-build",
"pkg-config",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "libsecret"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4af5a2342942fa42d706a424e9f9914287fb8317132750fd73a241140ac38c1"
dependencies = [
"bitflags",
"gio",
"glib",
"libc",
"libsecret-sys",
"once_cell",
]
[[package]]
name = "libsecret-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "287d2a0fcd95e4d7b0ac6fc9f802691a790d7e522138713b0cacebc4e63cab91"
dependencies = [
"gio-sys",
"glib-sys",
"gobject-sys",
"libc",
"pkg-config",
"system-deps",
]
[[package]]
name = "link-cplusplus"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8cae2cd7ba2f3f63938b9c724475dfb7b9861b545a90324476324ed21dbc8c8"
dependencies = [
"cc",
]
[[package]]
name = "lock_api"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "mio"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
]
[[package]]
name = "napi"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17ec66e60f000c78dd7c6215b6fa260e0591e09805024332bc5b3f55acc12244"
dependencies = [
"ctor",
"lazy_static",
"napi-sys",
"tokio",
"windows 0.30.0",
]
[[package]]
name = "napi-build"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b"
[[package]]
name = "napi-derive"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74ac5287a5e94a8728fc82d16c5127acc5eb5b8ad6404ef5f82d6a4ce8d5bdd2"
dependencies = [
"convert_case",
"napi-derive-backend",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "napi-derive-backend"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427f4f04525635cdf22005d1be62d6d671bcb5550d694a1efb480a315422b4af"
dependencies = [
"convert_case",
"once_cell",
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "napi-sys"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a385494dac3c52cbcacb393bb3b42669e7db8ab240c7ad5115f549eb061f2cc"
[[package]]
name = "ntapi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
dependencies = [
"winapi",
]
[[package]]
name = "num_cpus"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "parking_lot"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "pin-project-lite"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "proc-macro-crate"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
dependencies = [
"thiserror",
"toml",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96311ef4a16462c757bb6a39152c40f58f31cd2602a40fceb937e2bc34e6cbab"
[[package]]
name = "security-framework"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
dependencies = [
"bitflags",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "serde"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "socket2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "system-deps"
version = "6.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709"
dependencies = [
"cfg-expr",
"heck",
"pkg-config",
"toml",
"version-compare",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "version-compare"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "widestring"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0"
dependencies = [
"windows_aarch64_msvc 0.30.0",
"windows_i686_gnu 0.30.0",
"windows_i686_msvc 0.30.0",
"windows_x86_64_gnu 0.30.0",
"windows_x86_64_msvc 0.30.0",
]
[[package]]
name = "windows"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec"
dependencies = [
"windows_aarch64_msvc 0.32.0",
"windows_i686_gnu 0.32.0",
"windows_i686_msvc 0.32.0",
"windows_x86_64_gnu 0.32.0",
"windows_x86_64_msvc 0.32.0",
]
[[package]]
name = "windows-sys"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
dependencies = [
"windows_aarch64_msvc 0.32.0",
"windows_i686_gnu 0.32.0",
"windows_i686_msvc 0.32.0",
"windows_x86_64_gnu 0.32.0",
"windows_x86_64_msvc 0.32.0",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
[[package]]
name = "windows_i686_gnu"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
[[package]]
name = "windows_i686_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
[[package]]
name = "windows_i686_msvc"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
[[package]]
name = "windows_i686_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
[[package]]
name = "windows_x86_64_gnu"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"

43
desktop_native/Cargo.toml Normal file
View File

@ -0,0 +1,43 @@
[package]
edition = "2021"
exclude = ["index.node"]
license = "GPL-3.0"
name = "desktop_native"
version = "0.0.0"
[lib]
crate-type = ["cdylib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
napi = {version = "2", features = ["async"]}
napi-derive = "2"
scopeguard = "1.1.0"
tokio = {version = "1.17.0", features = ["full"]}
[build-dependencies]
napi-build = "1"
[target.'cfg(windows)'.dependencies]
widestring = "0.5.1"
windows = {version = "0.32.0", features = [
"alloc",
"Foundation",
"Storage_Streams",
"Win32_Foundation",
"Win32_Security_Credentials",
]}
[target.'cfg(windows)'.dev-dependencies]
keytar = "0.1.6"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9.3"
security-framework = "2.6.1"
security-framework-sys = "2.6.1"
[target.'cfg(target_os = "linux")'.dependencies]
gio = "0.15.6"
libsecret = "0.1.4"

22
desktop_native/build.js Normal file
View File

@ -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'});
});

5
desktop_native/build.rs Normal file
View File

@ -0,0 +1,5 @@
extern crate napi_build;
fn main() {
napi_build::setup();
}

15
desktop_native/index.d.ts vendored Normal file
View File

@ -0,0 +1,15 @@
/* tslint:disable */
/* eslint-disable */
/* auto-generated by NAPI-RS */
export namespace passwords {
/** Fetch the stored password from the keychain. */
export function getPassword(service: string, account: string): Promise<string>
/** Fetch the stored password from the keychain that was stored with Keytar. */
export function getPasswordKeytar(service: string, account: string): Promise<string>
/** Save the password to the keychain. Adds an entry if none exists otherwise updates the existing entry. */
export function setPassword(service: string, account: string, password: string): Promise<void>
/** Delete the stored password from the keychain. */
export function deletePassword(service: string, account: string): Promise<void>
}

241
desktop_native/index.js Normal file
View File

@ -0,0 +1,241 @@
const { existsSync, readFileSync } = require('fs')
const { join } = require('path')
const { platform, arch } = process
let nativeBinding = null
let localFileExisted = false
let loadError = null
function isMusl() {
// For Node 10
if (!process.report || typeof process.report.getReport !== 'function') {
try {
return readFileSync('/usr/bin/ldd', 'utf8').includes('musl')
} catch (e) {
return true
}
} else {
const { glibcVersionRuntime } = process.report.getReport().header
return !glibcVersionRuntime
}
}
switch (platform) {
case 'android':
switch (arch) {
case 'arm64':
localFileExisted = existsSync(join(__dirname, 'desktop_native.android-arm64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.android-arm64.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-android-arm64')
}
} catch (e) {
loadError = e
}
break
case 'arm':
localFileExisted = existsSync(join(__dirname, 'desktop_native.android-arm-eabi.node'))
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.android-arm-eabi.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-android-arm-eabi')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on Android ${arch}`)
}
break
case 'win32':
switch (arch) {
case 'x64':
localFileExisted = existsSync(
join(__dirname, 'desktop_native.win32-x64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.win32-x64-msvc.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-win32-x64-msvc')
}
} catch (e) {
loadError = e
}
break
case 'ia32':
localFileExisted = existsSync(
join(__dirname, 'desktop_native.win32-ia32-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.win32-ia32-msvc.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-win32-ia32-msvc')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'desktop_native.win32-arm64-msvc.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.win32-arm64-msvc.node')
} else {
nativeBinding = require('@bitwarden/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':
localFileExisted = existsSync(join(__dirname, 'desktop_native.darwin-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.darwin-x64.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-darwin-x64')
}
} catch (e) {
loadError = e
}
break
case 'arm64':
localFileExisted = existsSync(
join(__dirname, 'desktop_native.darwin-arm64.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.darwin-arm64.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-darwin-arm64')
}
} catch (e) {
loadError = e
}
break
default:
throw new Error(`Unsupported architecture on macOS: ${arch}`)
}
break
case 'freebsd':
if (arch !== 'x64') {
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
}
localFileExisted = existsSync(join(__dirname, 'desktop_native.freebsd-x64.node'))
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.freebsd-x64.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-freebsd-x64')
}
} catch (e) {
loadError = e
}
break
case 'linux':
switch (arch) {
case 'x64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'desktop_native.linux-x64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.linux-x64-musl.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-linux-x64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'desktop_native.linux-x64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.linux-x64-gnu.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-linux-x64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm64':
if (isMusl()) {
localFileExisted = existsSync(
join(__dirname, 'desktop_native.linux-arm64-musl.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.linux-arm64-musl.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-linux-arm64-musl')
}
} catch (e) {
loadError = e
}
} else {
localFileExisted = existsSync(
join(__dirname, 'desktop_native.linux-arm64-gnu.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.linux-arm64-gnu.node')
} else {
nativeBinding = require('@bitwarden/desktop_native-linux-arm64-gnu')
}
} catch (e) {
loadError = e
}
}
break
case 'arm':
localFileExisted = existsSync(
join(__dirname, 'desktop_native.linux-arm-gnueabihf.node')
)
try {
if (localFileExisted) {
nativeBinding = require('./desktop_native.linux-arm-gnueabihf.node')
} else {
nativeBinding = require('@bitwarden/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`)
}
const { passwords } = nativeBinding
module.exports.passwords = passwords

41
desktop_native/package-lock.json generated Normal file
View File

@ -0,0 +1,41 @@
{
"name": "@bitwarden/desktop_native",
"version": "0.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@bitwarden/desktop_native",
"version": "0.1.0",
"hasInstallScript": true,
"license": "GPL-3.0",
"devDependencies": {
"@napi-rs/cli": "^2.6.2"
}
},
"node_modules/@napi-rs/cli": {
"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"
},
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
}
}
},
"dependencies": {
"@napi-rs/cli": {
"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
}
}
}

View File

@ -0,0 +1,32 @@
{
"name": "@bitwarden/desktop_native",
"version": "0.1.0",
"description": "",
"main": "index.node",
"scripts": {
"build": "napi build --release --platform",
"build:debug": "napi build --platform",
"build:cross-platform": "node build.js",
"test": "cargo test"
},
"author": "",
"license": "GPL-3.0",
"devDependencies": {
"@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"
]
}
}
}

39
desktop_native/src/lib.rs Normal file
View File

@ -0,0 +1,39 @@
#[macro_use]
extern crate napi_derive;
mod password;
#[napi]
pub mod passwords {
/// Fetch the stored password from the keychain.
#[napi]
pub async fn get_password(service: String, account: String) -> napi::Result<String> {
super::password::get_password(&service, &account)
.map_err(|e| napi::Error::from_reason(e.to_string()))
}
/// Fetch the stored password from the keychain that was stored with Keytar.
#[napi]
pub async fn get_password_keytar(service: String, account: String) -> napi::Result<String> {
super::password::get_password_keytar(&service, &account)
.map_err(|e| napi::Error::from_reason(e.to_string()))
}
/// Save the password to the keychain. Adds an entry if none exists otherwise updates the existing entry.
#[napi]
pub async fn set_password(
service: String,
account: String,
password: String,
) -> napi::Result<()> {
super::password::set_password(&service, &account, &password)
.map_err(|e| napi::Error::from_reason(e.to_string()))
}
/// Delete the stored password from the keychain.
#[napi]
pub async fn delete_password(service: String, account: String) -> napi::Result<()> {
super::password::delete_password(&service, &account)
.map_err(|e| napi::Error::from_reason(e.to_string()))
}
}

View File

@ -0,0 +1,59 @@
use anyhow::Result;
use security_framework::passwords::{
delete_generic_password, get_generic_password, set_generic_password,
};
pub fn get_password(service: &str, account: &str) -> Result<String> {
let result = String::from_utf8(get_generic_password(&service, &account)?)?;
Ok(result)
}
pub fn get_password_keytar(service: &str, account: &str) -> Result<String> {
get_password(service, account)
}
pub fn set_password(service: &str, account: &str, password: &str) -> Result<()> {
let result = set_generic_password(&service, &account, password.as_bytes())?;
Ok(result)
}
pub fn delete_password(service: &str, account: &str) -> Result<()> {
let result = delete_generic_password(&service, &account)?;
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
scopeguard::defer!(delete_password("BitwardenTest", "BitwardenTest").unwrap_or({}););
set_password("BitwardenTest", "BitwardenTest", "Random").unwrap();
assert_eq!(
"Random",
get_password("BitwardenTest", "BitwardenTest").unwrap()
);
delete_password("BitwardenTest", "BitwardenTest").unwrap();
// Ensure password is deleted
match get_password("BitwardenTest", "BitwardenTest") {
Ok(_) => panic!("Got a result"),
Err(e) => assert_eq!(
"The specified item could not be found in the keychain.",
e.to_string()
),
}
}
#[test]
fn test_error_no_password() {
match get_password("Unknown", "Unknown") {
Ok(_) => panic!("Got a result"),
Err(e) => assert_eq!(
"The specified item could not be found in the keychain.",
e.to_string()
),
}
}
}

View File

@ -0,0 +1,5 @@
#[cfg_attr(target_os = "linux", path = "unix.rs")]
#[cfg_attr(target_os = "windows", path = "windows.rs")]
#[cfg_attr(target_os = "macos", path = "macos.rs")]
mod password;
pub use password::*;

View File

@ -0,0 +1,91 @@
use anyhow::{anyhow, Result};
use libsecret::{password_clear_sync, password_lookup_sync, password_store_sync, Schema};
use std::collections::HashMap;
pub fn get_password(service: &str, account: &str) -> Result<String> {
let res = password_lookup_sync(
Some(&get_schema()),
build_attributes(service, account),
gio::Cancellable::NONE,
)?;
match res {
Some(s) => Ok(String::from(s)),
None => Err(anyhow!("No password found")),
}
}
pub fn get_password_keytar(service: &str, account: &str) -> Result<String> {
get_password(service, account)
}
pub fn set_password(service: &str, account: &str, password: &str) -> Result<()> {
let result = password_store_sync(
Some(&get_schema()),
build_attributes(service, account),
Some(&libsecret::COLLECTION_DEFAULT),
&format!("{}/{}", service, account),
password,
gio::Cancellable::NONE,
)?;
Ok(result)
}
pub fn delete_password(service: &str, account: &str) -> Result<()> {
let result = password_clear_sync(
Some(&get_schema()),
build_attributes(service, account),
gio::Cancellable::NONE,
)?;
Ok(result)
}
fn get_schema() -> Schema {
let mut attributes = std::collections::HashMap::new();
attributes.insert("service", libsecret::SchemaAttributeType::String);
attributes.insert("account", libsecret::SchemaAttributeType::String);
libsecret::Schema::new(
"org.freedesktop.Secret.Generic",
libsecret::SchemaFlags::NONE,
attributes,
)
}
fn build_attributes<'a>(service: &'a str, account: &'a str) -> HashMap<&'a str, &'a str> {
let mut attributes = HashMap::new();
attributes.insert("service", service);
attributes.insert("account", account);
attributes
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
scopeguard::defer!(delete_password("BitwardenTest", "BitwardenTest").unwrap_or({}););
set_password("BitwardenTest", "BitwardenTest", "Random").unwrap();
assert_eq!(
"Random",
get_password("BitwardenTest", "BitwardenTest").unwrap()
);
delete_password("BitwardenTest", "BitwardenTest").unwrap();
// Ensure password is deleted
match get_password("BitwardenTest", "BitwardenTest") {
Ok(_) => panic!("Got a result"),
Err(e) => assert_eq!("No password found", e.to_string()),
}
}
#[test]
fn test_error_no_password() {
match get_password("BitwardenTest", "BitwardenTest") {
Ok(_) => panic!("Got a result"),
Err(e) => assert_eq!("No password found", e.to_string()),
}
}
}

View File

@ -0,0 +1,180 @@
use anyhow::{anyhow, Result};
use widestring::{U16CString, U16String};
use windows::Win32::{
Foundation::{GetLastError, ERROR_NOT_FOUND, FILETIME, PWSTR, WIN32_ERROR},
Security::Credentials::{
CredDeleteW, CredFree, CredReadW, CredWriteW, CREDENTIALW, CRED_FLAGS,
CRED_PERSIST_ENTERPRISE, CRED_TYPE_GENERIC,
},
};
const CRED_FLAGS_NONE: u32 = 0;
pub fn get_password<'a>(service: &str, account: &str) -> Result<String> {
let target_name = U16CString::from_str(target_name(service, account))?;
let mut credential: *mut CREDENTIALW = std::ptr::null_mut();
let credential_ptr = &mut credential;
let result = unsafe {
CredReadW(
PWSTR(target_name.as_ptr()),
CRED_TYPE_GENERIC.0,
CRED_FLAGS_NONE,
credential_ptr,
)
};
scopeguard::defer!({
unsafe { CredFree(credential as *mut _) };
});
if !result.as_bool() {
return Err(anyhow!(convert_error(unsafe { GetLastError() })));
}
let password = unsafe {
U16String::from_ptr(
(*credential).CredentialBlob as *const u16,
(*credential).CredentialBlobSize as usize / 2,
)
.to_string_lossy()
};
Ok(String::from(password))
}
// Remove this after sufficient releases
pub fn get_password_keytar<'a>(service: &str, account: &str) -> Result<String> {
let target_name = U16CString::from_str(target_name(service, account))?;
let mut credential: *mut CREDENTIALW = std::ptr::null_mut();
let credential_ptr = &mut credential;
let result = unsafe {
CredReadW(
PWSTR(target_name.as_ptr()),
CRED_TYPE_GENERIC.0,
CRED_FLAGS_NONE,
credential_ptr,
)
};
scopeguard::defer!({
unsafe { CredFree(credential as *mut _) };
});
if !result.as_bool() {
return Err(anyhow!(unsafe { GetLastError() }.0.to_string()));
}
let password = unsafe {
std::str::from_utf8_unchecked(std::slice::from_raw_parts(
(*credential).CredentialBlob,
(*credential).CredentialBlobSize as usize,
))
};
Ok(String::from(password))
}
pub fn set_password(service: &str, account: &str, password: &str) -> Result<()> {
let target_name = U16CString::from_str(target_name(service, account))?;
let user_name = U16CString::from_str(account)?;
let last_written = FILETIME {
dwLowDateTime: 0,
dwHighDateTime: 0,
};
let credential = U16CString::from_str(password)?;
let credential_len = password.len() as u32 * 2;
let credential = CREDENTIALW {
Flags: CRED_FLAGS(CRED_FLAGS_NONE),
Type: CRED_TYPE_GENERIC,
TargetName: PWSTR(target_name.as_ptr()),
Comment: PWSTR::default(),
LastWritten: last_written,
CredentialBlobSize: credential_len,
CredentialBlob: credential.as_ptr() as *mut u8,
Persist: CRED_PERSIST_ENTERPRISE,
AttributeCount: 0,
Attributes: std::ptr::null_mut(),
TargetAlias: PWSTR::default(),
UserName: PWSTR(user_name.as_ptr()),
};
let result = unsafe { CredWriteW(&credential, 0) };
if !result.as_bool() {
return Err(anyhow!(unsafe { GetLastError() }.0.to_string()));
}
Ok(())
}
pub fn delete_password(service: &str, account: &str) -> Result<()> {
let target_name = U16CString::from_str(target_name(service, account))?;
unsafe {
CredDeleteW(
PWSTR(target_name.as_ptr()),
CRED_TYPE_GENERIC.0,
CRED_FLAGS_NONE,
)
.ok()?
};
Ok(())
}
fn target_name(service: &str, account: &str) -> String {
format!("{}/{}", service, account)
}
// Convert the internal WIN32 errors to descriptive messages
fn convert_error(code: WIN32_ERROR) -> String {
match code {
ERROR_NOT_FOUND => String::from("Password not found."),
_ => code.0.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
scopeguard::defer!(delete_password("BitwardenTest", "BitwardenTest").unwrap_or({}););
set_password("BitwardenTest", "BitwardenTest", "Random").unwrap();
assert_eq!(
"Random",
get_password("BitwardenTest", "BitwardenTest").unwrap()
);
delete_password("BitwardenTest", "BitwardenTest").unwrap();
// Ensure password is deleted
match get_password("BitwardenTest", "BitwardenTest") {
Ok(_) => panic!("Got a result"),
Err(e) => assert_eq!("Password not found.", e.to_string()),
}
}
#[test]
fn test_get_password_keytar() {
scopeguard::defer!(delete_password("BitwardenTest", "BitwardenTest").unwrap_or({}););
keytar::set_password("BitwardenTest", "BitwardenTest", "HelloFromKeytar").unwrap();
assert_eq!(
"HelloFromKeytar",
get_password_keytar("BitwardenTest", "BitwardenTest").unwrap()
);
}
#[test]
fn test_error_no_password() {
match get_password("BitwardenTest", "BitwardenTest") {
Ok(_) => panic!("Got a result"),
Err(e) => assert_eq!("Password not found.", e.to_string()),
}
}
}

121
electron-builder.json Normal file
View File

@ -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/**/*"],
"publish": {
"provider": "generic",
"url": "https://artifacts.bitwarden.com/desktop"
},
"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"]
},
"protocols": [{ "name": "Bitwarden", "schemes": ["bitwarden"] }]
}

2
jslib

@ -1 +1 @@
Subproject commit 92a65b7b368a8dbf55350657674c90169b04c30b
Subproject commit 80c834b52a8b00f88250a47a9bbd40c269fc2cba

6264
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -26,8 +26,8 @@
"symlink:win": "rm -rf ./jslib && cmd /c mklink /J .\\jslib ..\\jslib",
"symlink:mac": "npm run symlink:lin",
"symlink:lin": "rm -rf ./jslib && ln -s ../jslib ./jslib",
"lint": "tslint 'src/**/*.ts' && prettier --check .",
"lint:fix": "tslint 'src/**/*.ts' --fix",
"lint": "eslint . && prettier --check .",
"lint:fix": "eslint . --fix",
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
"build:dev": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\"",
"build:main": "cross-env NODE_ENV=production webpack --config webpack.main.js",
@ -36,6 +36,7 @@
"build:renderer:dev": "cross-env NODE_ENV=development webpack --config webpack.renderer.js",
"build:renderer:watch": "cross-env NODE_ENV=development webpack --config webpack.renderer.js --watch",
"electron": "npm run build:main:dev && concurrently -k -n Main,Rend -c yellow,cyan \"electron --inspect=5858 ./build --watch\" \"npm run build:renderer:watch\"",
"electron:ignore": "npm run build:main:dev && concurrently -k -n Main,Rend -c yellow,cyan \"electron --inspect=5858 --ignore-certificate-errors ./build --watch\" \"npm run build:renderer:watch\"",
"clean:dist": "rimraf ./dist/*",
"clean:l10n": "git push origin --delete l10n_master",
"pack:dir": "npm run clean:dist && electron-builder --dir -p never",
@ -57,205 +58,18 @@
"publish:mac": "npm run build && npm run clean:dist && electron-builder --mac -p always",
"publish:mac:mas": "npm run dist:mac:mas && npm run upload:mas",
"publish:win": "npm run build && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always -c.win.certificateSubjectName=\"8bit Solutions LLC\"",
"publish:win:dev": "npm run build && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always",
"upload:mas": "xcrun altool --upload-app --type osx --file \"$(find ./dist/mas-universal/Bitwarden*.pkg)\" --username $APPLE_ID_USERNAME --password $APPLE_ID_PASSWORD",
"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"
],
"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
},
"nsisWeb": {
"oneClick": false,
"perMachine": true,
"allowToChangeInstallationDirectory": true,
"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",
"@types/node": "^16.11.12",
"@types/node-ipc": "^9.1.4",
"@typescript-eslint/eslint-plugin": "^5.12.1",
"@typescript-eslint/parser": "^5.12.1",
"clean-webpack-plugin": "^4.0.0",
"concurrently": "^6.0.2",
"copy-webpack-plugin": "^10.0.0",
@ -265,7 +79,10 @@
"electron-notarize": "^1.1.1",
"electron-rebuild": "^3.2.5",
"electron-reload": "^1.5.0",
"font-awesome": "4.7.0",
"eslint": "^8.9.0",
"eslint-config-prettier": "^8.4.0",
"eslint-import-resolver-typescript": "^2.5.0",
"eslint-plugin-import": "^2.25.4",
"html-loader": "^3.0.1",
"html-webpack-plugin": "^5.5.0",
"husky": "^7.0.4",
@ -279,8 +96,6 @@
"tapable": "^1.1.3",
"ts-loader": "^9.2.5",
"tsconfig-paths-webpack-plugin": "^3.5.1",
"tslint": "~6.1.0",
"tslint-loader": "^3.5.4",
"typescript": "4.3.5",
"webpack": "^5.64.4",
"webpack-cli": "^4.9.1",
@ -299,18 +114,23 @@
"@bitwarden/jslib-angular": "file:jslib/angular",
"@bitwarden/jslib-common": "file:jslib/common",
"@bitwarden/jslib-electron": "file:jslib/electron",
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
"forcefocus": "^1.1.0",
"keytar": "^7.9.0",
"ngx-toastr": "14.1.4",
"node-ipc": "^9.1.4",
"nord": "^0.2.1",
"regedit": "^3.0.3",
"rxjs": "^7.4.0",
"sweetalert2": "^10.16.6"
"sweetalert2": "^10.16.6",
"zone.js": "0.11.4"
},
"engines": {
"node": "~16",
"npm": "~8"
},
"lint-staged": {
"*": "prettier --ignore-unknown --write"
"./!(jslib)**": "prettier --ignore-unknown --write",
"*.ts": "eslint --fix"
}
}

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -1,8 +1,10 @@
/* eslint-disable @typescript-eslint/no-var-requires, no-console */
require("dotenv").config();
const path = require("path");
const fse = require("fs-extra");
const { notarize } = require("electron-notarize");
const { deepAssign } = require("builder-util");
const { notarize } = require("electron-notarize");
const fse = require("fs-extra");
exports.default = run;

1
scripts/dev/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
data

View File

@ -0,0 +1,19 @@
version: "3"
services:
minio:
image: minio/minio
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
# environment:
# MINIO_ROOT_USER: minioadmin
# MINIO_ROOT_PASSWORD: minioadmin
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
volumes:
- ./data:/data

View File

@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-var-requires, no-console */
exports.default = async function (configuration) {
if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && configuration.path.slice(-4) == ".exe") {
console.log(`[*] Signing file: ${configuration.path}`);

View File

@ -25,11 +25,7 @@
</div>
<div class="box">
<div class="box-header">
<button
type="button"
(click)="toggleCustom()"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
>
<button type="button" (click)="toggleCustom()" [attr.aria-expanded]="showCustom">
<i class="bwi bwi-plus-square" [hidden]="showCustom" aria-hidden="true"></i>
<i class="bwi bwi-minus-square" [hidden]="!showCustom" aria-hidden="true"></i>
{{ "customEnvironment" | i18n }}
@ -87,7 +83,7 @@
</div>
</div>
<div class="modal-footer">
<button appBlurClick type="submit" class="primary" appA11yTitle="{{ 'save' | i18n }}">
<button type="submit" class="primary" appA11yTitle="{{ 'save' | i18n }}">
<i class="bwi bwi-save-changes bwi-lg bwi-fw" aria-hidden="true"></i>
</button>
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>

View File

@ -1,11 +1,10 @@
import { Component } from "@angular/core";
import { EnvironmentComponent as BaseEnvironmentComponent } from "jslib-angular/components/environment.component";
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { EnvironmentComponent as BaseEnvironmentComponent } from "jslib-angular/components/environment.component";
@Component({
selector: "app-environment",
templateUrl: "environment.component.html",

View File

@ -21,11 +21,11 @@
</div>
</div>
<div class="buttons">
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
<button type="submit" class="btn primary block" [disabled]="form.loading">
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
<button type="button" routerLink="/login" class="btn block">{{ "cancel" | i18n }}</button>
</div>
</div>
</form>

View File

@ -1,13 +1,12 @@
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { HintComponent as BaseHintComponent } from "jslib-angular/components/hint.component";
@Component({
selector: "app-hint",
templateUrl: "hint.component.html",

View File

@ -30,13 +30,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePassword()"
>
<i
@ -44,7 +43,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
</button>
</div>
</div>
</div>
@ -58,17 +57,16 @@
type="button"
class="btn block"
[ngClass]="{ 'primary font-weight-bold': hideInput }"
appBlurClick
(click)="unlockBiometric()"
>
{{ biometricText | i18n }}
</button>
</div>
<div class="buttons-row">
<button type="submit" class="btn primary block" appBlurClick *ngIf="!hideInput">
<button type="submit" class="btn primary block" *ngIf="!hideInput">
<i class="bwi bwi-unlock" aria-hidden="true"></i> <b>{{ "unlock" | i18n }}</b>
</button>
<button type="button" class="btn block" appBlurClick (click)="logOut()">
<button type="button" class="btn block" (click)="logOut()">
{{ "logOut" | i18n }}
</button>
</div>

View File

@ -2,6 +2,7 @@ import { Component, NgZone, OnDestroy } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { ipcRenderer } from "electron";
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
import { ApiService } from "jslib-common/abstractions/api.service";
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
@ -14,8 +15,6 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { StateService } from "jslib-common/abstractions/state.service";
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
const BroadcasterSubscriptionId = "LockComponent";
@Component({

View File

@ -1,96 +1,104 @@
<div class="login-header">
<a
href="#"
appStopClick
(click)="settings()"
class="environment-urls-settings-icon"
attr.aria-label="{{ 'settings' | i18n }}"
<div id="login-page">
<div class="login-header">
<button
type="button"
appStopClick
(click)="settings()"
class="environment-urls-settings-icon"
attr.aria-label="{{ 'settings' | i18n }}"
>
<i class="bwi bwi-cog bwi-lg" aria-hidden="true"></i>
{{ "settings" | i18n }}
</button>
</div>
<form
id="login-page"
#form
(ngSubmit)="submit()"
[appApiAction]="formPromise"
attr.aria-hidden="{{ showingModal }}"
>
<i class="bwi bwi-cog bwi-lg" aria-hidden="true"></i>
{{ "settings" | i18n }}
</a>
</div>
<form
id="login-page"
#form
(ngSubmit)="submit()"
[appApiAction]="formPromise"
attr.aria-hidden="{{ showingModal }}"
>
<div id="content" class="content">
<img class="logo-image" alt="Bitwarden" />
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
<div class="box last">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="email">{{ "emailAddress" | i18n }}</label>
<input
id="email"
type="text"
name="Email"
[(ngModel)]="email"
required
appInputVerbatim="false"
/>
</div>
<div class="box-content-row box-content-row-flex" appBoxRow>
<div class="row-main">
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
<div id="content" class="content">
<img class="logo-image" alt="Bitwarden" />
<p class="lead">{{ "loginOrCreateNewAccount" | i18n }}</p>
<div class="box last">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="email">{{ "emailAddress" | i18n }}</label>
<input
id="masterPassword"
type="{{ showPassword ? 'text' : 'password' }}"
name="MasterPassword"
class="monospaced"
[(ngModel)]="masterPassword"
id="email"
type="text"
name="Email"
[(ngModel)]="email"
required
appInputVerbatim
appInputVerbatim="false"
/>
</div>
<div class="action-buttons">
<a
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="togglePassword()"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
<div class="box-content-row box-content-row-flex" appBoxRow>
<div class="row-main">
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
<input
id="masterPassword"
type="{{ showPassword ? 'text' : 'password' }}"
name="MasterPassword"
class="monospaced"
[(ngModel)]="masterPassword"
required
appInputVerbatim
/>
</div>
<div class="action-buttons">
<button
type="button"
class="row-btn"
appStopClick
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePassword()"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</button>
</div>
</div>
</div>
<div class="box-content-row" [hidden]="!showCaptcha()">
<iframe id="hcaptcha_iframe" height="80"></iframe>
</div>
<div class="box last" [hidden]="!showCaptcha()">
<div class="box-content">
<div class="box-content-row">
<iframe id="hcaptcha_iframe" height="80"></iframe>
</div>
</div>
</div>
</div>
<div class="buttons with-rows">
<div class="buttons-row">
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
<b [hidden]="form.loading"
><i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ "logIn" | i18n }}</b
<div class="buttons with-rows">
<div class="buttons-row">
<button type="submit" class="btn primary block" [disabled]="form.loading">
<b [hidden]="form.loading"
><i class="bwi bwi-sign-in" aria-hidden="true"></i> {{ "logIn" | i18n }}</b
>
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
<button type="button" routerLink="/register" class="btn block">
<i class="bwi bwi-pencil-square" aria-hidden="true"></i> {{ "createAccount" | i18n }}
</button>
</div>
<div class="buttons-row">
<button
type="button"
(click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')"
class="btn block"
>
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
<a routerLink="/register" class="btn block">
<i class="bwi bwi-pencil-square" aria-hidden="true"></i> {{ "createAccount" | i18n }}
</a>
<i class="bwi bwi-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
</button>
</div>
</div>
<div class="buttons-row">
<a (click)="launchSsoBrowser('desktop', 'bitwarden://sso-callback')" class="btn block">
<i class="bwi bwi-bank" aria-hidden="true"></i> {{ "enterpriseSingleSignOn" | i18n }}
</a>
<div class="sub-options">
<button type="button" routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</button>
</div>
</div>
<div class="sub-options">
<a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a>
</div>
</div>
</form>
</form>
</div>
<ng-template #environment></ng-template>

View File

@ -1,9 +1,8 @@
import { Component, NgZone, OnDestroy, ViewChild, ViewContainerRef } from "@angular/core";
import { Router } from "@angular/router";
import { EnvironmentComponent } from "./environment.component";
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
import { ModalService } from "jslib-angular/services/modal.service";
import { AuthService } from "jslib-common/abstractions/auth.service";
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
@ -16,9 +15,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { StateService } from "jslib-common/abstractions/state.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
import { ModalService } from "jslib-angular/services/modal.service";
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
import { EnvironmentComponent } from "./environment.component";
const BroadcasterSubscriptionId = "LoginComponent";
@ -32,7 +29,7 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy {
showingModal = false;
protected alwaysRememberEmail: boolean = true;
protected alwaysRememberEmail = true;
private deferFocus: boolean = null;

View File

@ -48,14 +48,13 @@
</div>
</div>
<div class="modal-footer">
<button type="button" class="primary" appBlurClick (click)="manage()" *ngIf="isPremium">
<button type="button" class="primary" (click)="manage()" *ngIf="isPremium">
<b>{{ "premiumManage" | i18n }}</b>
</button>
<button
#purchaseBtn
type="button"
class="primary"
appBlurClick
(click)="purchase()"
*ngIf="!isPremium"
[disabled]="purchaseBtn.loading"
@ -67,7 +66,6 @@
<button
#refreshBtn
type="button"
appBlurClick
(click)="refresh()"
[disabled]="refreshBtn.loading"
appA11yTitle="{{ 'premiumRefresh' | i18n }}"

View File

@ -1,13 +1,12 @@
import { Component } from "@angular/core";
import { PremiumComponent as BasePremiumComponent } from "jslib-angular/components/premium.component";
import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { PremiumComponent as BasePremiumComponent } from "jslib-angular/components/premium.component";
@Component({
selector: "app-premium",
templateUrl: "premium.component.html",

View File

@ -40,13 +40,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePassword(false)"
>
<i
@ -54,7 +53,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
</button>
</div>
</div>
<div class="progress">
@ -90,13 +89,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePassword(true)"
>
<i
@ -104,7 +102,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
</button>
</div>
</div>
<div class="box-content-row" appBoxRow>
@ -140,11 +138,11 @@
</div>
</div>
<div class="buttons">
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
<button type="submit" class="btn primary block" [disabled]="form.loading">
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
<button type="button" routerLink="/login" class="btn block">{{ "cancel" | i18n }}</button>
</div>
</div>
</form>

View File

@ -1,6 +1,7 @@
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
import { ApiService } from "jslib-common/abstractions/api.service";
import { AuthService } from "jslib-common/abstractions/auth.service";
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
@ -12,8 +13,6 @@ import { PasswordGenerationService } from "jslib-common/abstractions/passwordGen
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
const BroadcasterSubscriptionId = "RegisterComponent";
@Component({

View File

@ -7,7 +7,6 @@
type="submit"
class="btn primary block"
[disabled]="actionPromise"
appBlurClick
(click)="convert()"
>
<b [hidden]="continuing">{{ "removeMasterPassword" | i18n }}</b>
@ -17,7 +16,6 @@
type="button"
class="btn secondary block"
[disabled]="actionPromise"
appBlurClick
(click)="leave()"
>
<b [hidden]="leaving">{{ "leaveOrganization" | i18n }}</b>

View File

@ -56,13 +56,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePassword(false)"
>
<i
@ -70,7 +69,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
</button>
</div>
</div>
<div class="progress">
@ -108,13 +107,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePassword(true)"
>
<i
@ -122,7 +120,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
</button>
</div>
</div>
</div>

View File

@ -1,7 +1,7 @@
import { Component, NgZone, OnDestroy } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
import { ApiService } from "jslib-common/abstractions/api.service";
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
@ -15,8 +15,6 @@ import { SyncService } from "jslib-common/abstractions/sync.service";
const BroadcasterSubscriptionId = "SetPasswordComponent";
import { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
@Component({
selector: "app-set-password",
templateUrl: "set-password.component.html",

View File

@ -2,184 +2,186 @@
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-body form">
<ng-container *ngIf="currentUserEmail != null">
<div class="box">
<label class="settingsTitle">{{ "settingsTitle" | i18n: currentUserEmail }} </label>
<div class="box-content box-content-padded">
<h2>
<button
type="button"
class="box-header-expandable"
(click)="showSecurity = !showSecurity"
[attr.aria-expanded]="showSecurity"
>
{{ "security" | i18n }}
<i
*ngIf="!showSecurity"
class="fa fa-chevron-down fa-sm icon"
aria-hidden="true"
></i>
<i
*ngIf="showSecurity"
class="fa fa-chevron-up fa-sm icon"
aria-hidden="true"
></i>
</button>
</h2>
<ng-container *ngIf="showSecurity">
<app-vault-timeout-input
[vaultTimeouts]="vaultTimeouts"
[formControl]="vaultTimeout"
ngDefaultControl
></app-vault-timeout-input>
<div class="form-group">
<label>{{ "vaultTimeoutAction" | i18n }}</label>
<div class="radio radio-mt-2">
<label for="vaultTimeoutActionLock">
<input
type="radio"
name="VaultTimeoutAction"
id="vaultTimeoutActionLock"
value="lock"
[(ngModel)]="vaultTimeoutAction"
(change)="saveVaultTimeoutOptions()"
/>
{{ "lock" | i18n }}
</label>
</div>
<small class="help-block">{{ "vaultTimeoutActionLockDesc" | i18n }}</small>
<div class="radio">
<label for="vaultTimeoutActionLogOut">
<input
type="radio"
name="VaultTimeoutAction"
id="vaultTimeoutActionLogOut"
value="logOut"
[(ngModel)]="vaultTimeoutAction"
(change)="saveVaultTimeoutOptions()"
/>
{{ "logOut" | i18n }}
</label>
</div>
<small class="help-block">{{ "vaultTimeoutActionLogOutDesc" | i18n }}</small>
</div>
<div class="form-group">
<div class="checkbox">
<label for="pin">
<input
id="pin"
type="checkbox"
name="PIN"
[(ngModel)]="pin"
(change)="updatePin()"
/>
{{ "unlockWithPin" | i18n }}
</label>
</div>
</div>
<div class="form-group" *ngIf="supportsBiometric">
<div class="checkbox">
<label for="biometric">
<input
id="biometric"
type="checkbox"
name="biometric"
[checked]="biometric"
(change)="updateBiometric()"
/>
{{ biometricText | i18n }}
</label>
</div>
</div>
<div class="form-group" *ngIf="supportsBiometric">
<div class="checkbox">
<label for="noAutoPromptBiometrics">
<input
id="noAutoPromptBiometrics"
type="checkbox"
name="noAutoPromptBiometrics"
[(ngModel)]="noAutoPromptBiometrics"
[disabled]="!biometric"
(change)="updateNoAutoPromptBiometrics()"
/>
{{ noAutoPromptBiometricsText | i18n }}
</label>
</div>
</div>
</ng-container>
</div>
<div class="box">
<div class="box-header">
{{ "settingsTitle" | i18n: currentUserEmail }}
</div>
<div class="box">
<div class="box-content box-content-padded">
<h2>
<button
type="button"
class="box-header-expandable"
(click)="showAccountPreferences = !showAccountPreferences"
[attr.aria-expanded]="showAccountPreferences"
>
{{ "accountPreferences" | i18n }}
<i
*ngIf="!showAccountPreferences"
class="fa fa-chevron-down fa-sm icon"
aria-hidden="true"
></i>
<i
*ngIf="showAccountPreferences"
class="fa fa-chevron-up fa-sm icon"
aria-hidden="true"
></i>
</button>
</h2>
<ng-container *ngIf="showAccountPreferences">
<div class="form-group">
<label for="clearClipboard">{{ "clearClipboard" | i18n }}</label>
<select
id="clearClipboard"
name="ClearClipboard"
[(ngModel)]="clearClipboard"
(change)="saveClearClipboard()"
>
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">
{{ o.name }}
</option>
</select>
<small class="help-block">{{ "clearClipboardDesc" | i18n }}</small>
<div class="box-content box-content-padded">
<h2>
<button
id="app-settings"
type="button"
class="box-header-expandable"
(click)="showSecurity = !showSecurity"
[attr.aria-expanded]="showSecurity"
appAutofocus
>
{{ "security" | i18n }}
<i
*ngIf="!showSecurity"
class="bwi bwi-angle-down bwi-sm icon"
aria-hidden="true"
></i>
<i
*ngIf="showSecurity"
class="bwi bwi-chevron-up bwi-sm icon"
aria-hidden="true"
></i>
</button>
</h2>
<ng-container *ngIf="showSecurity">
<app-vault-timeout-input
[vaultTimeouts]="vaultTimeouts"
[formControl]="vaultTimeout"
ngDefaultControl
></app-vault-timeout-input>
<div class="form-group">
<label>{{ "vaultTimeoutAction" | i18n }}</label>
<div class="radio radio-mt-2">
<label for="vaultTimeoutActionLock">
<input
type="radio"
name="VaultTimeoutAction"
id="vaultTimeoutActionLock"
value="lock"
[(ngModel)]="vaultTimeoutAction"
(change)="saveVaultTimeoutOptions()"
/>
{{ "lock" | i18n }}
</label>
</div>
<div class="form-group">
<div class="checkbox">
<label for="minimizeOnCopyToClipboard">
<input
id="minimizeOnCopyToClipboard"
type="checkbox"
name="MinimizeOnCopyToClipboard"
[(ngModel)]="minimizeOnCopyToClipboard"
(change)="saveMinOnCopyToClipboard()"
/>
{{ "minimizeOnCopyToClipboard" | i18n }}
</label>
</div>
<small class="help-block">{{ "minimizeOnCopyToClipboardDesc" | i18n }}</small>
<small class="help-block">{{ "vaultTimeoutActionLockDesc" | i18n }}</small>
<div class="radio">
<label for="vaultTimeoutActionLogOut">
<input
type="radio"
name="VaultTimeoutAction"
id="vaultTimeoutActionLogOut"
value="logOut"
[(ngModel)]="vaultTimeoutAction"
(change)="saveVaultTimeoutOptions()"
/>
{{ "logOut" | i18n }}
</label>
</div>
<div class="form-group">
<div class="checkbox">
<label for="disableFavicons">
<input
id="disableFavicons"
type="checkbox"
name="DisableFavicons"
[(ngModel)]="disableFavicons"
(change)="saveFavicons()"
/>
{{ "disableFavicon" | i18n }}
</label>
</div>
<small class="help-block">{{ "disableFaviconDesc" | i18n }}</small>
<small class="help-block">{{ "vaultTimeoutActionLogOutDesc" | i18n }}</small>
</div>
<div class="form-group">
<div class="checkbox">
<label for="pin">
<input
id="pin"
type="checkbox"
name="PIN"
[(ngModel)]="pin"
(change)="updatePin()"
/>
{{ "unlockWithPin" | i18n }}
</label>
</div>
</ng-container>
</div>
</div>
<div class="form-group" *ngIf="supportsBiometric">
<div class="checkbox">
<label for="biometric">
<input
id="biometric"
type="checkbox"
name="biometric"
[checked]="biometric"
(change)="updateBiometric()"
/>
{{ biometricText | i18n }}
</label>
</div>
</div>
<div class="form-group" *ngIf="supportsBiometric">
<div class="checkbox">
<label for="noAutoPromptBiometrics">
<input
id="noAutoPromptBiometrics"
type="checkbox"
name="noAutoPromptBiometrics"
[(ngModel)]="noAutoPromptBiometrics"
[disabled]="!biometric"
(change)="updateNoAutoPromptBiometrics()"
/>
{{ noAutoPromptBiometricsText | i18n }}
</label>
</div>
</div>
</ng-container>
</div>
</ng-container>
</div>
<div class="box">
<div class="box-content box-content-padded">
<h2>
<button
type="button"
class="box-header-expandable"
(click)="showAccountPreferences = !showAccountPreferences"
[attr.aria-expanded]="showAccountPreferences"
>
{{ "accountPreferences" | i18n }}
<i
*ngIf="!showAccountPreferences"
class="bwi bwi-angle-down bwi-sm icon"
aria-hidden="true"
></i>
<i
*ngIf="showAccountPreferences"
class="bwi bwi-chevron-up bwi-sm icon"
aria-hidden="true"
></i>
</button>
</h2>
<ng-container *ngIf="showAccountPreferences">
<div class="form-group">
<label for="clearClipboard">{{ "clearClipboard" | i18n }}</label>
<select
id="clearClipboard"
name="ClearClipboard"
[(ngModel)]="clearClipboard"
(change)="saveClearClipboard()"
>
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">
{{ o.name }}
</option>
</select>
<small class="help-block">{{ "clearClipboardDesc" | i18n }}</small>
</div>
<div class="form-group">
<div class="checkbox">
<label for="minimizeOnCopyToClipboard">
<input
id="minimizeOnCopyToClipboard"
type="checkbox"
name="MinimizeOnCopyToClipboard"
[(ngModel)]="minimizeOnCopyToClipboard"
(change)="saveMinOnCopyToClipboard()"
/>
{{ "minimizeOnCopyToClipboard" | i18n }}
</label>
</div>
<small class="help-block">{{ "minimizeOnCopyToClipboardDesc" | i18n }}</small>
</div>
<div class="form-group">
<div class="checkbox">
<label for="disableFavicons">
<input
id="disableFavicons"
type="checkbox"
name="DisableFavicons"
[(ngModel)]="disableFavicons"
(change)="saveFavicons()"
/>
{{ "disableFavicon" | i18n }}
</label>
</div>
<small class="help-block">{{ "disableFaviconDesc" | i18n }}</small>
</div>
</ng-container>
</div>
</div>
<div class="box">
<div class="box-content box-content-padded">
<h2>
@ -192,12 +194,12 @@
{{ "appPreferences" | i18n }}
<i
*ngIf="!showAppPreferences"
class="fa fa-chevron-down fa-sm icon"
class="bwi bwi-angle-down bwi-sm icon"
aria-hidden="true"
></i>
<i
*ngIf="showAppPreferences"
class="fa fa-chevron-up fa-sm icon"
class="bwi bwi-chevron-up bwi-sm icon"
aria-hidden="true"
></i>
</button>

View File

@ -2,24 +2,20 @@ import { Component, OnInit } from "@angular/core";
import { FormControl } from "@angular/forms";
import { debounceTime } from "rxjs/operators";
import { DeviceType } from "jslib-common/enums/deviceType";
import { ThemeType } from "jslib-common/enums/themeType";
import { ModalService } from "jslib-angular/services/modal.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
import { ModalService } from "jslib-angular/services/modal.service";
import { SetPinComponent } from "../components/set-pin.component";
import { DeviceType } from "jslib-common/enums/deviceType";
import { StorageLocation } from "jslib-common/enums/storageLocation";
import { ThemeType } from "jslib-common/enums/themeType";
import { Utils } from "jslib-common/misc/utils";
import { isWindowsStore } from "jslib-electron/utils";
import { StorageLocation } from "jslib-common/enums/storageLocation";
import { SetPinComponent } from "../components/set-pin.component";
@Component({
selector: "app-settings",
@ -28,15 +24,15 @@ import { StorageLocation } from "jslib-common/enums/storageLocation";
export class SettingsComponent implements OnInit {
vaultTimeoutAction: string;
pin: boolean = null;
disableFavicons: boolean = false;
enableBrowserIntegration: boolean = false;
enableBrowserIntegrationFingerprint: boolean = false;
enableMinToTray: boolean = false;
enableCloseToTray: boolean = false;
enableTray: boolean = false;
showMinToTray: boolean = false;
startToTray: boolean = false;
minimizeOnCopyToClipboard: boolean = false;
disableFavicons = false;
enableBrowserIntegration = false;
enableBrowserIntegrationFingerprint = false;
enableMinToTray = false;
enableCloseToTray = false;
enableTray = false;
showMinToTray = false;
startToTray = false;
minimizeOnCopyToClipboard = false;
locale: string;
vaultTimeouts: any[];
localeOptions: any[];
@ -50,9 +46,9 @@ export class SettingsComponent implements OnInit {
noAutoPromptBiometrics: boolean;
noAutoPromptBiometricsText: string;
alwaysShowDock: boolean;
showAlwaysShowDock: boolean = false;
showAlwaysShowDock = false;
openAtLogin: boolean;
requireEnableTray: boolean = false;
requireEnableTray = false;
enableTrayText: string;
enableTrayDescText: string;
@ -65,9 +61,9 @@ export class SettingsComponent implements OnInit {
vaultTimeout: FormControl = new FormControl(null);
showSecurity: boolean = true;
showAccountPreferences: boolean = true;
showAppPreferences: boolean = true;
showSecurity = true;
showAccountPreferences = true;
showAppPreferences = true;
currentUserEmail: string;

View File

@ -1,7 +1,7 @@
import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
import { ApiService } from "jslib-common/abstractions/api.service";
import { AuthService } from "jslib-common/abstractions/auth.service";
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
@ -13,8 +13,6 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { StateService } from "jslib-common/abstractions/state.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
@Component({
selector: "app-sso",
templateUrl: "sso.component.html",

View File

@ -7,8 +7,8 @@
{{ "twoStepOptions" | i18n }}
</div>
<div class="box-content">
<a
href="#"
<button
type="button"
appStopClick
*ngFor="let p of providers"
class="box-content-row"
@ -17,11 +17,11 @@
<img [src]="'images/two-factor/' + p.type + '.png'" alt="" class="img-right" />
<span class="text">{{ p.name }}</span>
<span class="detail">{{ p.description }}</span>
</a>
<a href="#" appStopClick class="box-content-row" (click)="recover()">
</button>
<button type="button" appStopClick class="box-content-row" (click)="recover()">
<span class="text">{{ "recoveryCodeTitle" | i18n }}</span>
<span class="detail">{{ "recoveryCodeDesc" | i18n }}</span>
</a>
</button>
</div>
</div>
</div>

View File

@ -1,11 +1,10 @@
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import { AuthService } from "jslib-common/abstractions/auth.service";
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jslib-angular/components/two-factor-options.component";
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
@Component({
selector: "app-two-factor-options",
@ -13,11 +12,11 @@ import { TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent } from "jsli
})
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
constructor(
authService: AuthService,
twoFactorService: TwoFactorService,
router: Router,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService
) {
super(authService, router, i18nService, platformUtilsService, window);
super(twoFactorService, router, i18nService, platformUtilsService, window);
}
}

View File

@ -5,7 +5,7 @@
[appApiAction]="formPromise"
attr.aria-hidden="{{ showingModal }}"
>
<div class="content">
<div id="content" class="content">
<h1>{{ title }}</h1>
<p *ngIf="selectedProviderType === providerType.Authenticator">
{{ "enterVerificationCodeApp" | i18n }}
@ -100,12 +100,18 @@
</div>
</div>
</div>
<div class="box last" [hidden]="!showCaptcha()">
<div class="box-content">
<div class="box-content-row">
<iframe id="hcaptcha_iframe" height="80"></iframe>
</div>
</div>
</div>
<div class="buttons">
<button
type="submit"
class="btn primary block"
[disabled]="form.loading"
appBlurClick
*ngIf="
selectedProviderType != null &&
selectedProviderType !== providerType.Duo &&
@ -117,22 +123,21 @@
>
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
<a routerLink="/login" class="btn block">{{ "cancel" | i18n }}</a>
<button type="button" routerLink="/login" class="btn block">{{ "cancel" | i18n }}</button>
</div>
<div class="sub-options">
<a href="#" appStopClick (click)="anotherMethod()" role="button">{{
"useAnotherTwoStepMethod" | i18n
}}</a>
<a
href="#"
<button type="button" appStopClick (click)="anotherMethod()">
{{ "useAnotherTwoStepMethod" | i18n }}
</button>
<button
type="button"
appStopClick
(click)="sendEmail(true)"
[appApiAction]="emailPromise"
role="button"
*ngIf="selectedProviderType === providerType.Email"
>
{{ "sendVerificationCodeEmailAgain" | i18n }}
</a>
</button>
</div>
</div>
</form>

View File

@ -1,12 +1,10 @@
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { TwoFactorOptionsComponent } from "./two-factor-options.component";
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
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";
@ -14,10 +12,10 @@ import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
import { ModalService } from "jslib-angular/services/modal.service";
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
import { TwoFactorOptionsComponent } from "./two-factor-options.component";
@Component({
selector: "app-two-factor",
@ -40,7 +38,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
private modalService: ModalService,
stateService: StateService,
route: ActivatedRoute,
logService: LogService
logService: LogService,
twoFactorService: TwoFactorService,
appIdService: AppIdService
) {
super(
authService,
@ -52,7 +52,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
environmentService,
stateService,
route,
logService
logService,
twoFactorService,
appIdService
);
super.onSuccessfulLogin = () => {
return syncService.fullSync(true);
@ -81,4 +83,12 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
modal.close();
});
}
async submit() {
await super.submit();
if (this.captchaSiteKey) {
const content = document.getElementById("content") as HTMLDivElement;
content.setAttribute("style", "width:335px");
}
}
}

View File

@ -36,13 +36,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePassword(false)"
>
<i
@ -50,7 +49,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
</button>
</div>
</div>
<div class="progress">
@ -83,13 +82,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePassword(true)"
>
<i
@ -97,7 +95,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
</button>
</div>
</div>
</div>
@ -114,11 +112,11 @@
</div>
</div>
<div class="buttons">
<button type="submit" class="btn primary block" [disabled]="form.loading" appBlurClick>
<button type="submit" class="btn primary block" [disabled]="form.loading">
<b [hidden]="form.loading">{{ "submit" | i18n }}</b>
<i class="bwi bwi-spinner bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
<a (click)="logOut()" class="btn block">{{ "logOut" | i18n }}</a>
<button type="button" (click)="logOut()" class="btn block">{{ "logOut" | i18n }}</button>
</div>
</div>
</form>

View File

@ -1,5 +1,6 @@
import { Component } from "@angular/core";
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "jslib-angular/components/update-temp-password.component";
import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
@ -11,8 +12,6 @@ import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "jslib-angular/components/update-temp-password.component";
interface MasterPasswordScore {
Color: string;
Text: string;

View File

@ -10,7 +10,6 @@
name="VaultTimeout"
formControlName="vaultTimeout"
class="form-control"
appAutofocus
>
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{ o.name }}</option>
</select>

View File

@ -3,6 +3,7 @@ import { RouterModule, Routes } from "@angular/router";
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
import { LockGuardService } from "jslib-angular/services/lock-guard.service";
import { LoginGuardService } from "../services/loginGuard.service";
import { HintComponent } from "./accounts/hint.component";
@ -14,9 +15,7 @@ import { SetPasswordComponent } from "./accounts/set-password.component";
import { SsoComponent } from "./accounts/sso.component";
import { TwoFactorComponent } from "./accounts/two-factor.component";
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
import { SendComponent } from "./send/send.component";
import { VaultComponent } from "./vault/vault.component";
const routes: Routes = [

View File

@ -11,10 +11,8 @@ import { DomSanitizer } from "@angular/platform-browser";
import { Router } from "@angular/router";
import { IndividualConfig, ToastrService } from "ngx-toastr";
import { PremiumComponent } from "./accounts/premium.component";
import { SettingsComponent } from "./accounts/settings.component";
import { PasswordGeneratorHistoryComponent } from "./vault/password-generator-history.component";
import { ModalRef } from "jslib-angular/components/modal/modal.ref";
import { ModalService } from "jslib-angular/services/modal.service";
import { AuthService } from "jslib-common/abstractions/auth.service";
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
import { CipherService } from "jslib-common/abstractions/cipher.service";
@ -37,22 +35,28 @@ import { SyncService } from "jslib-common/abstractions/sync.service";
import { SystemService } from "jslib-common/abstractions/system.service";
import { TokenService } from "jslib-common/abstractions/token.service";
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
import { AuthenticationStatus } from "jslib-common/enums/authenticationStatus";
import { CipherType } from "jslib-common/enums/cipherType";
import { MenuUpdateRequest } from "../main/menu/menu.updater";
import { PremiumComponent } from "./accounts/premium.component";
import { SettingsComponent } from "./accounts/settings.component";
import { ExportComponent } from "./vault/export.component";
import { FolderAddEditComponent } from "./vault/folder-add-edit.component";
import { PasswordGeneratorComponent } from "./vault/password-generator.component";
import { ModalRef } from "jslib-angular/components/modal/modal.ref";
import { ModalService } from "jslib-angular/services/modal.service";
import { MenuUpdateRequest } from "src/main/menu.updater";
import { GeneratorComponent } from "./vault/generator.component";
import { PasswordGeneratorHistoryComponent } from "./vault/password-generator-history.component";
const BroadcasterSubscriptionId = "AppComponent";
const IdleTimeout = 60000 * 10; // 10 minutes
const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours
const systemTimeoutOptions = {
onLock: -2,
onSuspend: -3,
onIdle: -4,
};
@Component({
selector: "app-root",
styles: [],
@ -62,7 +66,7 @@ const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours
<ng-template #passwordHistory></ng-template>
<ng-template #appFolderAddEdit></ng-template>
<ng-template #exportVault></ng-template>
<ng-template #appPasswordGenerator></ng-template>
<ng-template #appGenerator></ng-template>
<app-header></app-header>
<div id="container">
<div class="loading" *ngIf="loading">
@ -81,8 +85,8 @@ export class AppComponent implements OnInit {
exportVaultModalRef: ViewContainerRef;
@ViewChild("appFolderAddEdit", { read: ViewContainerRef, static: true })
folderAddEditModalRef: ViewContainerRef;
@ViewChild("appPasswordGenerator", { read: ViewContainerRef, static: true })
passwordGeneratorModalRef: ViewContainerRef;
@ViewChild("appGenerator", { read: ViewContainerRef, static: true })
generatorModalRef: ViewContainerRef;
loading = false;
@ -90,6 +94,7 @@ export class AppComponent implements OnInit {
private modal: ModalRef = null;
private idleTimer: number = null;
private isIdle = false;
private activeUserId: string = null;
constructor(
private broadcasterService: BroadcasterService,
@ -122,21 +127,18 @@ export class AppComponent implements OnInit {
) {}
ngOnInit() {
let activeUserId: string = null;
this.stateService.activeAccount.subscribe((userId) => {
activeUserId = userId;
this.activeUserId = userId;
});
this.ngZone.runOutsideAngular(() => {
setTimeout(async () => {
await this.updateAppMenu();
}, 1000);
if (activeUserId != null) {
window.ontouchstart = () => this.recordActivity(activeUserId);
window.onmousedown = () => this.recordActivity(activeUserId);
window.onscroll = () => this.recordActivity(activeUserId);
window.onkeypress = () => this.recordActivity(activeUserId);
}
window.ontouchstart = () => this.recordActivity();
window.onmousedown = () => this.recordActivity();
window.onscroll = () => this.recordActivity();
window.onkeypress = () => this.recordActivity();
});
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
@ -161,7 +163,7 @@ export class AppComponent implements OnInit {
this.router.navigate(["login"]);
break;
case "logout":
this.loading = true;
this.loading = message.userId == null || message.userId === this.activeUserId;
await this.logOut(!!message.expired, message.userId);
this.loading = false;
break;
@ -204,7 +206,7 @@ export class AppComponent implements OnInit {
case "openPremium":
await this.openModal<PremiumComponent>(PremiumComponent, this.premiumRef);
break;
case "showFingerprintPhrase":
case "showFingerprintPhrase": {
const fingerprint = await this.cryptoService.getFingerprint(
await this.stateService.getUserId()
);
@ -215,11 +217,10 @@ export class AppComponent implements OnInit {
this.i18nService.t("close")
);
if (result) {
this.platformUtilsService.launchUri(
"https://help.bitwarden.com/article/fingerprint-phrase/"
);
this.platformUtilsService.launchUri("https://bitwarden.com/help/fingerprint-phrase/");
}
break;
}
case "openPasswordHistory":
await this.openModal<PasswordGeneratorHistoryComponent>(
PasswordGeneratorHistoryComponent,
@ -239,7 +240,7 @@ export class AppComponent implements OnInit {
queryParams: { code: message.code, state: message.state },
});
break;
case "premiumRequired":
case "premiumRequired": {
const premiumConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("premiumRequiredDesc"),
this.i18nService.t("premiumRequired"),
@ -250,7 +251,8 @@ export class AppComponent implements OnInit {
await this.openModal<PremiumComponent>(PremiumComponent, this.premiumRef);
}
break;
case "emailVerificationRequired":
}
case "emailVerificationRequired": {
const emailVerificationConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("emailVerificationRequiredDesc"),
this.i18nService.t("emailVerificationRequired"),
@ -259,10 +261,11 @@ export class AppComponent implements OnInit {
);
if (emailVerificationConfirmed) {
this.platformUtilsService.launchUri(
"https://bitwarden.com/help/article/create-bitwarden-account/"
"https://bitwarden.com/help/create-bitwarden-account/"
);
}
break;
}
case "syncVault":
try {
await this.syncService.fullSync(true, true);
@ -315,20 +318,22 @@ export class AppComponent implements OnInit {
case "newFolder":
await this.addFolder();
break;
case "openPasswordGenerator":
// openPasswordGenerator has extended functionality if called in the vault
case "openGenerator":
// openGenerator has extended functionality if called in the vault
if (!this.router.url.includes("vault")) {
await this.openPasswordGenerator();
await this.openGenerator();
}
break;
case "convertAccountToKeyConnector":
this.router.navigate(["/remove-password"]);
break;
case "switchAccount":
case "switchAccount": {
if (message.userId != null) {
await this.stateService.setActiveUser(message.userId);
}
const locked = await this.vaultTimeoutService.isLocked(message.userId);
const locked =
(await this.authService.getAuthStatus(message.userId)) ===
AuthenticationStatus.Locked;
if (locked) {
this.messagingService.send("locked", { userId: message.userId });
} else {
@ -339,6 +344,16 @@ export class AppComponent implements OnInit {
this.router.navigate(["vault"]);
}
break;
}
case "systemSuspended":
await this.checkForSystemTimeout(systemTimeoutOptions.onSuspend);
break;
case "systemLocked":
await this.checkForSystemTimeout(systemTimeoutOptions.onLock);
break;
case "systemIdle":
await this.checkForSystemTimeout(systemTimeoutOptions.onIdle);
break;
}
});
});
@ -390,15 +405,15 @@ export class AppComponent implements OnInit {
});
}
async openPasswordGenerator() {
async openGenerator() {
if (this.modal != null) {
this.modal.close();
}
[this.modal] = await this.modalService.openViewRef(
PasswordGeneratorComponent,
this.folderAddEditModalRef,
(comp) => (comp.showSelect = false)
GeneratorComponent,
this.generatorModalRef,
(comp) => (comp.comingFromAddEdit = false)
);
this.modal.onClosed.subscribe(() => {
@ -424,7 +439,8 @@ export class AppComponent implements OnInit {
isAuthenticated: await this.stateService.getIsAuthenticated({
userId: userId,
}),
isLocked: await this.vaultTimeoutService.isLocked(userId),
isLocked:
(await this.authService.getAuthStatus(userId)) === AuthenticationStatus.Locked,
email: stateAccounts[i].profile.email,
userId: stateAccounts[i].profile.userId,
};
@ -441,28 +457,24 @@ export class AppComponent implements OnInit {
}
private async logOut(expired: boolean, userId?: string) {
if (!userId) {
userId = await this.stateService.getUserId();
}
const userBeingLoggedOut = await this.stateService.getUserId({ userId: userId });
await Promise.all([
this.eventService.uploadEvents(userId),
this.syncService.setLastSync(new Date(0), userId),
this.tokenService.clearToken(userId),
this.cryptoService.clearKeys(userId),
this.settingsService.clear(userId),
this.cipherService.clear(userId),
this.folderService.clear(userId),
this.collectionService.clear(userId),
this.passwordGenerationService.clear(userId),
this.vaultTimeoutService.clear(userId),
this.policyService.clear(userId),
this.eventService.uploadEvents(userBeingLoggedOut),
this.syncService.setLastSync(new Date(0), userBeingLoggedOut),
this.cryptoService.clearKeys(userBeingLoggedOut),
this.settingsService.clear(userBeingLoggedOut),
this.cipherService.clear(userBeingLoggedOut),
this.folderService.clear(userBeingLoggedOut),
this.collectionService.clear(userBeingLoggedOut),
this.passwordGenerationService.clear(userBeingLoggedOut),
this.vaultTimeoutService.clear(userBeingLoggedOut),
this.policyService.clear(userBeingLoggedOut),
this.keyConnectorService.clear(),
]);
await this.stateService.setBiometricLocked(true, { userId: userId });
await this.stateService.setBiometricLocked(true, { userId: userBeingLoggedOut });
if (userId == null || userId === (await this.stateService.getUserId())) {
if (userBeingLoggedOut === this.activeUserId) {
this.searchService.clearIndex();
this.authService.logOut(async () => {
if (expired) {
@ -475,25 +487,30 @@ export class AppComponent implements OnInit {
});
}
await this.stateService.clean({ userId: userId });
const preLogoutActiveUserId = this.activeUserId;
await this.stateService.clean({ userId: userBeingLoggedOut });
if (this.stateService.activeAccount.getValue() == null) {
if (this.activeUserId == null) {
this.router.navigate(["login"]);
} else {
} else if (preLogoutActiveUserId !== this.activeUserId) {
this.messagingService.send("switchAccount");
}
await this.updateAppMenu();
}
private async recordActivity(userId: string) {
private async recordActivity() {
if (this.activeUserId == null) {
return;
}
const now = new Date().getTime();
if (this.lastActivity != null && now - this.lastActivity < 250) {
return;
}
this.lastActivity = now;
await this.stateService.setLastActive(now, { userId: userId });
await this.stateService.setLastActive(now, { userId: this.activeUserId });
// Idle states
if (this.isIdle) {
@ -573,14 +590,37 @@ export class AppComponent implements OnInit {
}
private async reloadProcess(): Promise<void> {
const accounts = Object.keys(this.stateService.accounts.getValue());
if (accounts.length > 0) {
for (const userId of accounts) {
if (!(await this.vaultTimeoutService.isLocked(userId))) {
return;
const accounts = this.stateService.accounts.getValue();
if (accounts != null) {
const keys = Object.keys(accounts);
if (keys.length > 0) {
for (const userId of keys) {
if ((await this.authService.getAuthStatus(userId)) === AuthenticationStatus.Unlocked) {
return;
}
}
}
}
await this.systemService.startProcessReload();
}
private async checkForSystemTimeout(timeout: number): Promise<void> {
for (const userId in this.stateService.accounts.getValue()) {
if (userId == null) {
continue;
}
const options = await this.getVaultTimeoutOptions(userId);
if (options[0] === timeout) {
options[1] === "logOut"
? this.logOut(false, userId)
: await this.vaultTimeoutService.lock(true, userId);
}
}
}
private async getVaultTimeoutOptions(userId: string): Promise<[number, string]> {
const timeout = await this.stateService.getVaultTimeout({ userId: userId });
const action = await this.stateService.getVaultTimeoutAction({ userId: userId });
return [timeout, action];
}
}

View File

@ -4,17 +4,66 @@ import { A11yModule } from "@angular/cdk/a11y";
import { DragDropModule } from "@angular/cdk/drag-drop";
import { OverlayModule } from "@angular/cdk/overlay";
import { ScrollingModule } from "@angular/cdk/scrolling";
import { DatePipe } from "@angular/common";
import { DatePipe, registerLocaleData } from "@angular/common";
import localeAf from "@angular/common/locales/af";
import localeAz from "@angular/common/locales/az";
import localeBe from "@angular/common/locales/be";
import localeBg from "@angular/common/locales/bg";
import localeBn from "@angular/common/locales/bn";
import localeBs from "@angular/common/locales/bs";
import localeCa from "@angular/common/locales/ca";
import localeCs from "@angular/common/locales/cs";
import localeDa from "@angular/common/locales/da";
import localeDe from "@angular/common/locales/de";
import localeEl from "@angular/common/locales/el";
import localeEnGb from "@angular/common/locales/en-GB";
import localeEnIn from "@angular/common/locales/en-IN";
import localeEo from "@angular/common/locales/eo";
import localeEs from "@angular/common/locales/es";
import localeEt from "@angular/common/locales/et";
import localeFa from "@angular/common/locales/fa";
import localeFi from "@angular/common/locales/fi";
import localeFil from "@angular/common/locales/fil";
import localeFr from "@angular/common/locales/fr";
import localeHe from "@angular/common/locales/he";
import localeHi from "@angular/common/locales/hi";
import localeHr from "@angular/common/locales/hr";
import localeHu from "@angular/common/locales/hu";
import localeId from "@angular/common/locales/id";
import localeIt from "@angular/common/locales/it";
import localeJa from "@angular/common/locales/ja";
import localeKa from "@angular/common/locales/ka";
import localeKm from "@angular/common/locales/km";
import localeKn from "@angular/common/locales/kn";
import localeKo from "@angular/common/locales/ko";
import localeLv from "@angular/common/locales/lv";
import localeMl from "@angular/common/locales/ml";
import localeNb from "@angular/common/locales/nb";
import localeNl from "@angular/common/locales/nl";
import localeNn from "@angular/common/locales/nn";
import localePl from "@angular/common/locales/pl";
import localePtBr from "@angular/common/locales/pt";
import localePtPt from "@angular/common/locales/pt-PT";
import localeRo from "@angular/common/locales/ro";
import localeRu from "@angular/common/locales/ru";
import localeSi from "@angular/common/locales/si";
import localeSk from "@angular/common/locales/sk";
import localeSl from "@angular/common/locales/sl";
import localeSr from "@angular/common/locales/sr";
import localeMe from "@angular/common/locales/sr-Latn-ME";
import localeSv from "@angular/common/locales/sv";
import localeTh from "@angular/common/locales/th";
import localeTr from "@angular/common/locales/tr";
import localeUk from "@angular/common/locales/uk";
import localeVi from "@angular/common/locales/vi";
import localeZhCn from "@angular/common/locales/zh-Hans";
import localeZhTw from "@angular/common/locales/zh-Hant";
import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import "zone.js/dist/zone";
import { AppRoutingModule } from "./app-routing.module";
import { ServicesModule } from "./services.module";
import { AppComponent } from "./app.component";
import { JslibModule } from "jslib-angular/jslib.module";
import { EnvironmentComponent } from "./accounts/environment.component";
import { HintComponent } from "./accounts/hint.component";
@ -30,29 +79,19 @@ import { TwoFactorOptionsComponent } from "./accounts/two-factor-options.compone
import { TwoFactorComponent } from "./accounts/two-factor.component";
import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.component";
import { VaultTimeoutInputComponent } from "./accounts/vault-timeout-input.component";
import { AvatarComponent } from "jslib-angular/components/avatar.component";
import { CalloutComponent } from "jslib-angular/components/callout.component";
import { IconComponent } from "jslib-angular/components/icon.component";
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
import { A11yTitleDirective } from "jslib-angular/directives/a11y-title.directive";
import { ApiActionDirective } from "jslib-angular/directives/api-action.directive";
import { AutofocusDirective } from "jslib-angular/directives/autofocus.directive";
import { BlurClickDirective } from "jslib-angular/directives/blur-click.directive";
import { BoxRowDirective } from "jslib-angular/directives/box-row.directive";
import { CipherListVirtualScroll } from "jslib-angular/directives/cipherListVirtualScroll.directive";
import { FallbackSrcDirective } from "jslib-angular/directives/fallback-src.directive";
import { InputVerbatimDirective } from "jslib-angular/directives/input-verbatim.directive";
import { SelectCopyDirective } from "jslib-angular/directives/select-copy.directive";
import { StopClickDirective } from "jslib-angular/directives/stop-click.directive";
import { StopPropDirective } from "jslib-angular/directives/stop-prop.directive";
import { TrueFalseValueDirective } from "jslib-angular/directives/true-false-value.directive";
import { ColorPasswordPipe } from "jslib-angular/pipes/color-password.pipe";
import { I18nPipe } from "jslib-angular/pipes/i18n.pipe";
import { SearchCiphersPipe } from "jslib-angular/pipes/search-ciphers.pipe";
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 { 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";
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/services.module";
import { AddEditCustomFieldsComponent } from "./vault/add-edit-custom-fields.component";
import { AddEditComponent } from "./vault/add-edit.component";
import { AttachmentsComponent } from "./vault/attachments.component";
@ -60,79 +99,21 @@ import { CiphersComponent } from "./vault/ciphers.component";
import { CollectionsComponent } from "./vault/collections.component";
import { ExportComponent } from "./vault/export.component";
import { FolderAddEditComponent } from "./vault/folder-add-edit.component";
import { GeneratorComponent } from "./vault/generator.component";
import { GroupingsComponent } from "./vault/groupings.component";
import { PasswordGeneratorHistoryComponent } from "./vault/password-generator-history.component";
import { PasswordGeneratorComponent } from "./vault/password-generator.component";
import { PasswordHistoryComponent } from "./vault/password-history.component";
import { ShareComponent } from "./vault/share.component";
import { VaultComponent } from "./vault/vault.component";
import { ViewCustomFieldsComponent } from "./vault/view-custom-fields.component";
import { ViewComponent } from "./vault/view.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 { AccountSwitcherComponent } from "./layout/account-switcher.component";
import { HeaderComponent } from "./layout/header.component";
import { NavComponent } from "./layout/nav.component";
import { SearchComponent } from "./layout/search/search.component";
import { PasswordRepromptComponent } from "./components/password-reprompt.component";
import { SetPinComponent } from "./components/set-pin.component";
import { VerifyMasterPasswordComponent } from "./components/verify-master-password.component";
import { registerLocaleData } from "@angular/common";
import localeAf from "@angular/common/locales/af";
import localeAz from "@angular/common/locales/az";
import localeBe from "@angular/common/locales/be";
import localeBg from "@angular/common/locales/bg";
import localeBn from "@angular/common/locales/bn";
import localeCa from "@angular/common/locales/ca";
import localeCs from "@angular/common/locales/cs";
import localeDa from "@angular/common/locales/da";
import localeDe from "@angular/common/locales/de";
import localeEl from "@angular/common/locales/el";
import localeEnGb from "@angular/common/locales/en-GB";
import localeEnIn from "@angular/common/locales/en-IN";
import localeEs from "@angular/common/locales/es";
import localeEt from "@angular/common/locales/et";
import localeFa from "@angular/common/locales/fa";
import localeFi from "@angular/common/locales/fi";
import localeFr from "@angular/common/locales/fr";
import localeHe from "@angular/common/locales/he";
import localeHr from "@angular/common/locales/hr";
import localeHu from "@angular/common/locales/hu";
import localeId from "@angular/common/locales/id";
import localeIt from "@angular/common/locales/it";
import localeJa from "@angular/common/locales/ja";
import localeKn from "@angular/common/locales/kn";
import localeKo from "@angular/common/locales/ko";
import localeLv from "@angular/common/locales/lv";
import localeMl from "@angular/common/locales/ml";
import localeNb from "@angular/common/locales/nb";
import localeNl from "@angular/common/locales/nl";
import localePl from "@angular/common/locales/pl";
import localePtBr from "@angular/common/locales/pt";
import localePtPt from "@angular/common/locales/pt-PT";
import localeRo from "@angular/common/locales/ro";
import localeRu from "@angular/common/locales/ru";
import localeSk from "@angular/common/locales/sk";
import localeSr from "@angular/common/locales/sr";
import localeMe from "@angular/common/locales/sr-Latn-ME";
import localeSv from "@angular/common/locales/sv";
import localeTh from "@angular/common/locales/th";
import localeTr from "@angular/common/locales/tr";
import localeUk from "@angular/common/locales/uk";
import localeVi from "@angular/common/locales/vi";
import localeZhCn from "@angular/common/locales/zh-Hans";
import localeZhTw from "@angular/common/locales/zh-Hant";
registerLocaleData(localeAf, "af");
registerLocaleData(localeAz, "az");
registerLocaleData(localeBe, "be");
registerLocaleData(localeBg, "bg");
registerLocaleData(localeBn, "bn");
registerLocaleData(localeBs, "bs");
registerLocaleData(localeCa, "ca");
registerLocaleData(localeCs, "cs");
registerLocaleData(localeDa, "da");
@ -140,17 +121,22 @@ registerLocaleData(localeDe, "de");
registerLocaleData(localeEl, "el");
registerLocaleData(localeEnGb, "en-GB");
registerLocaleData(localeEnIn, "en-IN");
registerLocaleData(localeEo, "eo");
registerLocaleData(localeEs, "es");
registerLocaleData(localeEt, "et");
registerLocaleData(localeFa, "fa");
registerLocaleData(localeFi, "fi");
registerLocaleData(localeFil, "fil");
registerLocaleData(localeFr, "fr");
registerLocaleData(localeHe, "he");
registerLocaleData(localeHi, "hi");
registerLocaleData(localeHr, "hr");
registerLocaleData(localeHu, "hu");
registerLocaleData(localeId, "id");
registerLocaleData(localeIt, "it");
registerLocaleData(localeJa, "ja");
registerLocaleData(localeKa, "ka");
registerLocaleData(localeKm, "km");
registerLocaleData(localeKn, "kn");
registerLocaleData(localeKo, "ko");
registerLocaleData(localeLv, "lv");
@ -158,12 +144,15 @@ registerLocaleData(localeMe, "me");
registerLocaleData(localeMl, "ml");
registerLocaleData(localeNb, "nb");
registerLocaleData(localeNl, "nl");
registerLocaleData(localeNn, "nn");
registerLocaleData(localePl, "pl");
registerLocaleData(localePtBr, "pt-BR");
registerLocaleData(localePtPt, "pt-PT");
registerLocaleData(localeRo, "ro");
registerLocaleData(localeRu, "ru");
registerLocaleData(localeSi, "si");
registerLocaleData(localeSk, "sk");
registerLocaleData(localeSl, "sl");
registerLocaleData(localeSr, "sr");
registerLocaleData(localeSv, "sv");
registerLocaleData(localeTh, "th");
@ -175,58 +164,43 @@ registerLocaleData(localeZhTw, "zh-TW");
@NgModule({
imports: [
A11yModule,
AppRoutingModule,
BrowserAnimationsModule,
BrowserModule,
DragDropModule,
FormsModule,
ReactiveFormsModule,
ServicesModule,
BitwardenToastModule.forRoot({
maxOpened: 5,
autoDismiss: true,
closeButton: true,
}),
ScrollingModule,
A11yModule,
JslibModule,
OverlayModule,
ReactiveFormsModule,
ScrollingModule,
ServicesModule,
],
declarations: [
A11yTitleDirective,
AccountSwitcherComponent,
AddEditComponent,
AddEditCustomFieldsComponent,
ApiActionDirective,
AppComponent,
AttachmentsComponent,
AutofocusDirective,
BlurClickDirective,
BoxRowDirective,
CalloutComponent,
CipherListVirtualScroll,
CiphersComponent,
CollectionsComponent,
ColorPasswordPipe,
EnvironmentComponent,
ExportComponent,
FallbackSrcDirective,
FolderAddEditComponent,
GroupingsComponent,
HeaderComponent,
HintComponent,
I18nPipe,
IconComponent,
InputVerbatimDirective,
LockComponent,
LoginComponent,
NavComponent,
PasswordGeneratorComponent,
GeneratorComponent,
PasswordGeneratorHistoryComponent,
PasswordHistoryComponent,
PasswordRepromptComponent,
PremiumComponent,
RegisterComponent,
RemovePasswordComponent,
SearchCiphersPipe,
SelectCopyDirective,
SearchComponent,
SendAddEditComponent,
SendComponent,
SendEffluxDatesComponent,
@ -235,21 +209,14 @@ registerLocaleData(localeZhTw, "zh-TW");
SettingsComponent,
ShareComponent,
SsoComponent,
StopClickDirective,
StopPropDirective,
TrueFalseValueDirective,
TwoFactorComponent,
TwoFactorOptionsComponent,
UpdateTempPasswordComponent,
UserVerificationComponent,
VaultComponent,
VaultTimeoutInputComponent,
VerifyMasterPasswordComponent,
ViewComponent,
ViewCustomFieldsComponent,
HeaderComponent,
AccountSwitcherComponent,
AvatarComponent,
SearchComponent,
],
providers: [DatePipe],
bootstrap: [AppComponent],

View File

@ -19,13 +19,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePassword()"
>
<i
@ -33,7 +32,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
</button>
</div>
</div>
</div>
@ -43,7 +42,7 @@
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
<button type="submit" class="btn btn-primary btn-submit">
<span>{{ "ok" | i18n }}</span>
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">

View File

@ -22,12 +22,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPin"
(click)="toggleVisibility()"
>
<i
@ -35,7 +35,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPin, 'bwi-eye-slash': showPin }"
></i>
</a>
</button>
</div>
</div>
</div>

View File

@ -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 {}

View File

@ -1,9 +1,12 @@
<a
<button
class="account-switcher"
(click)="toggle()"
cdkOverlayOrigin
#trigger="cdkOverlayOrigin"
[hidden]="!showSwitcher"
aria-haspopup="menu"
aria-controls="cdk-overlay-container"
[attr.aria-expanded]="isOpen"
>
<ng-container *ngIf="activeAccountEmail != null; else noActiveAccount">
<app-avatar
@ -13,6 +16,7 @@
[fontSize]="14"
[dynamic]="true"
*ngIf="activeAccountEmail != null"
aria-hidden="true"
></app-avatar>
<span>{{ activeAccountEmail }}</span>
</ng-container>
@ -24,26 +28,34 @@
aria-hidden="true"
[ngClass]="{ 'bwi-angle-down': !isOpen, 'bwi-chevron-up': isOpen }"
></i>
</a>
</button>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="trigger"
[cdkConnectedOverlayHasBackdrop]="true"
[cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
(backdropClick)="toggle()"
(backdropClick)="close()"
(detach)="close()"
[cdkConnectedOverlayOpen]="showSwitcher && isOpen"
[cdkConnectedOverlayPositions]="overlayPostition"
cdkConnectedOverlayMinWidth="250px"
>
<div class="account-switcher-dropdown" [@transformPanel]="'open'">
<div
class="account-switcher-dropdown"
[@transformPanel]="'open'"
cdkTrapFocus
cdkTrapFocusAutoCapture
role="dialog"
aria-modal="true"
>
<div class="accounts" *ngIf="numberOfAccounts > 0">
<a
<button
*ngFor="let a of accounts | keyvalue"
class="account"
[ngClass]="{ active: a.value.profile.authenticationStatus == 'active' }"
href="#"
(mousedown)="switch(a.key)"
(click)="switch(a.key)"
appA11yTitle="{{ 'loggedInAsOn' | i18n: a.value.profile.email:a.value.serverUrl }}"
attr.aria-label="{{ 'switchAccount' | i18n }}"
>
<app-avatar
[data]="a.value.profile.email"
@ -52,30 +64,33 @@
[fontSize]="14"
[dynamic]="true"
*ngIf="a.value.profile.email != null"
aria-hidden="true"
></app-avatar>
<div class="accountInfo">
<span class="email">{{ a.value.profile.email }}</span>
<span class="server" *ngIf="a.value.serverUrl != 'bitwarden.com'">{{
<span class="email" aria-hidden="true">{{ a.value.profile.email }}</span>
<span class="server" aria-hidden="true" *ngIf="a.value.serverUrl != 'bitwarden.com'">{{
a.value.serverUrl
}}</span>
<span class="status">{{ a.value.profile.authenticationStatus }}</span>
<span class="status" aria-hidden="true">{{
(a.value.profile.authenticationStatus === authStatus.Unlocked ? "unlocked" : "locked")
| i18n
}}</span>
</div>
<i
class="bwi bwi-unlock bwi-2x text-muted"
*ngIf="a.value.profile.authenticationStatus == 'unlocked'"
class="bwi bwi-2x text-muted"
[ngClass]="
a.value.profile.authenticationStatus == authStatus.Unlocked ? 'bwi-unlock' : 'bwi-lock'
"
aria-hidden="true"
></i>
<i
class="bwi bwi-lock bwi-2x text-muted"
*ngIf="a.value.profile.authenticationStatus == 'locked'"
></i>
</a>
</button>
</div>
<ng-container *ngIf="activeAccountEmail != null">
<div class="border" *ngIf="numberOfAccounts > 0"></div>
<ng-container *ngIf="numberOfAccounts < 4">
<a class="add" routerLink="/login" (click)="addAccount()">
<button class="add" routerLink="/login" (click)="addAccount()">
<i class="bwi bwi-plus" aria-hidden="true"></i> {{ "addAccount" | i18n }}
</a>
</button>
</ng-container>
<ng-container *ngIf="numberOfAccounts == 4">
<span class="accountLimitReached">{{ "accountSwitcherLimitReached" | i18n }} </span>

View File

@ -2,13 +2,11 @@ import { animate, state, style, transition, trigger } from "@angular/animations"
import { ConnectedPosition } from "@angular/cdk/overlay";
import { Component, OnInit } from "@angular/core";
import { AuthService } from "jslib-common/abstractions/auth.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
import { AuthenticationStatus } from "jslib-common/enums/authenticationStatus";
import { Utils } from "jslib-common/misc/utils";
import { Account } from "jslib-common/models/domain/account";
export class SwitcherAccount extends Account {
@ -51,10 +49,11 @@ export class SwitcherAccount extends Account {
],
})
export class AccountSwitcherComponent implements OnInit {
isOpen: boolean = false;
isOpen = false;
accounts: { [userId: string]: SwitcherAccount } = {};
activeAccountEmail: string;
serverUrl: string;
authStatus = AuthenticationStatus;
overlayPostition: ConnectedPosition[] = [
{
originX: "end",
@ -80,22 +79,16 @@ export class AccountSwitcherComponent implements OnInit {
constructor(
private stateService: StateService,
private vaultTimeoutService: VaultTimeoutService,
private authService: AuthService,
private messagingService: MessagingService
) {}
async ngOnInit(): Promise<void> {
this.stateService.accounts.subscribe(async (accounts) => {
this.stateService.accounts.subscribe(async (accounts: { [userId: string]: Account }) => {
for (const userId in accounts) {
if (userId === (await this.stateService.getUserId())) {
accounts[userId].profile.authenticationStatus = AuthenticationStatus.Active;
} else {
accounts[userId].profile.authenticationStatus = (await this.vaultTimeoutService.isLocked(
userId
))
? AuthenticationStatus.Locked
: AuthenticationStatus.Unlocked;
}
accounts[userId].profile.authenticationStatus = await this.authService.getAuthStatus(
userId
);
}
this.accounts = await this.createSwitcherAccounts(accounts);
@ -107,14 +100,18 @@ export class AccountSwitcherComponent implements OnInit {
this.isOpen = !this.isOpen;
}
close() {
this.isOpen = false;
}
async switch(userId: string) {
this.toggle();
this.close();
this.messagingService.send("switchAccount", { userId: userId });
}
async addAccount() {
this.toggle();
this.close();
await this.stateService.setActiveUser(null);
}

View File

@ -1,5 +1,13 @@
<ng-container *ngFor="let item of items">
<a [routerLink]="item.link" class="btn primary" routerLinkActive="active" [title]="item.label">
<button
type="button"
[routerLink]="item.link"
class="btn primary"
routerLinkActive="active"
[title]="item.label"
#rla="routerLinkActive"
[attr.aria-pressed]="rla.isActive"
>
<i class="bwi" [ngClass]="item.icon"></i>{{ item.label }}
</a>
</button>
</ng-container>

View File

@ -1,4 +1,5 @@
import { Component } from "@angular/core";
import { I18nService } from "jslib-common/abstractions/i18n.service";
@Component({

View File

@ -93,21 +93,20 @@
</div>
<div class="box">
<div class="box-header">
{{ "options" | i18n }}
<a
<button
type="button"
class="toggle"
href="#"
appStopClick
appBlurClick
role="button"
(click)="toggleOptions()"
[attr.aria-expanded]="showOptions"
>
{{ "options" | i18n }}
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="{ 'bwi-angle-down': !showOptions, 'bwi-chevron-up': showOptions }"
></i>
</a>
</button>
</div>
</div>
<div [hidden]="!showOptions">
@ -157,13 +156,12 @@
/>
</div>
<div class="action-buttons">
<a
<button
type="button"
class="row-btn"
href="#"
appStopClick
appBlurClick
role="button"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
[attr.aria-pressed]="showPassword"
(click)="togglePasswordVisible()"
[disabled]="disableSend"
>
@ -172,7 +170,7 @@
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</a>
</button>
</div>
</div>
</div>
@ -253,7 +251,6 @@
</div>
<div class="footer">
<button
appBlurClick
type="submit"
class="primary btn-submit"
appA11yTitle="{{ 'save' | i18n }}"
@ -263,12 +260,11 @@
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span><i class="bwi bwi-save-changes bwi-lg bwi-fw" aria-hidden="true"></i></span>
</button>
<button appBlurClick type="button" (click)="cancel()" [disabled]="form.loading">
<button type="button" (click)="cancel()" [disabled]="form.loading">
{{ "cancel" | i18n }}
</button>
<div class="right">
<button
appBlurClick
type="button"
(click)="copyLinkToClipboard(link)"
appA11yTitle="{{ 'copySendLinkToClipboard' | i18n }}"
@ -278,7 +274,6 @@
</button>
<button
#deleteBtn
appBlurClick
type="button"
(click)="delete()"
class="danger"

View File

@ -1,7 +1,7 @@
import { DatePipe } from "@angular/common";
import { Component } from "@angular/core";
import { AddEditComponent as BaseAddEditComponent } from "jslib-angular/components/send/add-edit.component";
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
@ -11,8 +11,6 @@ import { PolicyService } from "jslib-common/abstractions/policy.service";
import { SendService } from "jslib-common/abstractions/send.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { AddEditComponent as BaseAddEditComponent } from "jslib-angular/components/send/add-edit.component";
@Component({
selector: "app-send-add-edit",
templateUrl: "add-edit.component.html",

View File

@ -1,14 +1,11 @@
import { DatePipe } from "@angular/common";
import { Component, OnChanges } from "@angular/core";
import { ControlContainer, NgForm } from "@angular/forms";
import { EffluxDatesComponent as BaseEffluxDatesComponent } from "jslib-angular/components/send/efflux-dates.component";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { EffluxDatesComponent as BaseEffluxDatesComponent } from "jslib-angular/components/send/efflux-dates.component";
@Component({
selector: "app-send-efflux-dates",
templateUrl: "efflux-dates.component.html",

View File

@ -5,26 +5,41 @@
<h2 class="sr-only">{{ "filters" | i18n }}</h2>
<ul>
<li [ngClass]="{ active: selectedAll }">
<a href="#" appStopClick appBlurClick (click)="selectAll()">
<button
type="button"
appStopClick
(click)="selectAll()"
[attr.aria-pressed]="selectedAll"
>
<i class="bwi bwi-fw bwi-filter" aria-hidden="true"></i>&nbsp;{{ "allSends" | i18n }}
</a>
</button>
</li>
</ul>
<h2>{{ "types" | i18n }}</h2>
<ul>
<li [ngClass]="{ active: selectedType === sendType.Text }">
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.Text)">
<button
type="button"
appStopClick
(click)="selectType(sendType.Text)"
[attr.aria-pressed]="selectedType === sendType.Text"
>
<i class="bwi bwi-fw bwi-file-text" aria-hidden="true"></i>&nbsp;{{
"sendTypeText" | i18n
}}
</a>
</button>
</li>
<li [ngClass]="{ active: selectedType === sendType.File }">
<a href="#" appStopClick appBlurClick (click)="selectType(sendType.File)">
<button
type="button"
appStopClick
(click)="selectType(sendType.File)"
[attr.aria-pressed]="selectedType === sendType.File"
>
<i class="bwi bwi-fw bwi-file" aria-hidden="true"></i>&nbsp;{{
"sendTypeFile" | i18n
}}
</a>
</button>
</li>
</ul>
</div>
@ -36,20 +51,21 @@
<div id="items" class="items">
<div class="content">
<div class="list" *ngIf="filteredSends.length">
<a
<button
*ngFor="let s of filteredSends"
appStopClick
(click)="selectSend(s.id)"
title="{{ 'viewItem' | i18n }}"
(contextmenu)="viewSendMenu(s)"
[ngClass]="{ active: s.id === sendId }"
[attr.aria-pressed]="s.id === sendId"
class="flex-list-item"
>
<div class="item-icon" aria-hidden="true">
<span class="item-icon" aria-hidden="true">
<i class="bwi bwi-fw bwi-lg" [ngClass]="s.type == 0 ? 'bwi-file-text' : 'bwi-file'"></i>
</div>
<div class="item-content">
<div class="item-title">
</span>
<span class="item-content">
<span class="item-title">
{{ s.name }}
<span class="title-badges">
<ng-container *ngIf="s.disabled">
@ -98,10 +114,10 @@
<span class="sr-only">{{ "pendingDeletion" | i18n }}</span>
</ng-container>
</span>
</div>
</span>
<span class="item-details">{{ s.deletionDate | date }}</span>
</div>
</a>
</span>
</button>
</div>
<div class="no-items" *ngIf="!filteredSends.length">
<i class="bwi bwi-spinner bwi-spin bwi-3x" *ngIf="!loaded" aria-hidden="true"></i>
@ -112,12 +128,7 @@
</div>
</div>
<div class="footer">
<button
appBlurClick
(click)="addSend()"
class="block primary"
appA11yTitle="{{ 'addItem' | i18n }}"
>
<button (click)="addSend()" class="block primary" appA11yTitle="{{ 'addItem' | i18n }}">
<i class="bwi bwi-plus bwi-lg" aria-hidden="true"></i>
</button>
</div>

Some files were not shown because too many files have changed in this diff Show More