1
0
mirror of https://github.com/bitwarden/web synced 2025-12-06 00:03:28 +00:00

Compare commits

...

176 Commits

Author SHA1 Message Date
Joseph Flinn
ec80782d8f Version check typo (#1717)
* fixing dumb issue

* Fixing another dumb mistake

(cherry picked from commit ad0512e344)
2022-05-31 18:09:11 -07:00
Joseph Flinn
3acee89646 Updating the path for the Release Version Check (#1716)
(cherry picked from commit 8fe9504cd1)
2022-05-31 17:50:37 -07:00
Joseph Flinn
589faf3608 Updating the version check to the new Github action (#1714)
* Updating the version check to the new Github action

* Update .github/workflows/release.yml

Co-authored-by: Micaiah Martin <77340197+mimartin12@users.noreply.github.com>

* switching action branch tag to commit tag

Co-authored-by: Micaiah Martin <77340197+mimartin12@users.noreply.github.com>
(cherry picked from commit b8aa25b981)
2022-05-31 16:13:41 -07:00
github-actions[bot]
639cc461ae Bumped version to 2022.05.0 (#1715)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit 5d09ddbc8d)
2022-05-31 16:13:31 -07:00
Thomas Rittson
4562920d30 Update jslib 2022-05-27 13:12:37 +10:00
Matt Gibson
c6f4e4de64 update jslib 2022-05-26 13:19:49 -04:00
Matt Gibson
f323963a4e [PS-655] Add Organization_SponsorshipsSynced event type. (#1696)
* Add `Organization_SponsorshipsSynced` event type.

Update events display to handle events triggered by installations rather than users

* Update jslib

(cherry picked from commit 5a78853de5)
2022-05-26 13:06:46 -04:00
Thomas Rittson
f6c8b6d8cb [EC-177] Update Key Connector error message (#1705)
* Update Key Connector error message

* Update jslib
2022-05-26 07:32:52 +10:00
Robyn MacCallum
11f34be18e [SG-345] Switch in-line "+" add org button to more explicit version (#1703)
* switch in-line plus add org button to more explicit version

* Add spaces to line up text better
2022-05-25 10:23:43 -04:00
Robyn MacCallum
647e9555d0 Fix 'Link SSO' not appearing (#1702) 2022-05-23 19:44:54 -04:00
Matt Gibson
ad7b6e6388 Revert "[PS-655] Add Organization_SponsorshipsSynced event type. (#1696)"
This reverts commit aa29a07678.
2022-05-19 11:52:40 -04:00
Matt Gibson
718ca197b5 Revert "update jslib"
This reverts commit 338d593cd1.
2022-05-19 11:52:36 -04:00
Matt Gibson
338d593cd1 update jslib 2022-05-19 11:41:13 -04:00
Matt Gibson
aa29a07678 [PS-655] Add Organization_SponsorshipsSynced event type. (#1696)
* Add `Organization_SponsorshipsSynced` event type.

Update events display to handle events triggered by installations rather than users

* Update jslib

(cherry picked from commit 5a78853de5)
2022-05-19 11:39:06 -04:00
Robyn MacCallum
0722522801 Make spacing match other rows in vault filter (#1695)
* Make spacing match other rows in vault filter

* Add spaces in headings
2022-05-18 10:14:39 -04:00
Oscar Hinton
5731c6449b [EC-203] Add logic for falling back to the users vault (#1688)
* Add logic for falling back to the users vault

* Update error message

* Add jira ticket
2022-05-17 12:47:21 -04:00
Robyn MacCallum
eaf083be44 Fix switcher not appearing for users affected by single org policy (#1689) 2022-05-17 10:06:20 -04:00
Robyn MacCallum
8435c85c3c [SG-16] Fix various small bugs (#1686)
* Fix all items showing in No Folder

* Fix folders not showing for orgs

* Fix missing toasts if there are errors with org options
2022-05-17 09:23:12 -04:00
Justin Baur
3d1c2dad74 Fix Defects (#1683) 2022-05-16 09:47:24 -04:00
Jake Fink
2f632f7096 change revoke text (#1684) 2022-05-16 09:15:45 -04:00
Kyle Spearrin
9b63c81b56 remove hostname from simplelogin. update jslib (#1680) 2022-05-13 15:36:48 -04:00
Addison Beck
34cb463e94 [fix] Various Trash filter bugs from EUVR (#1681)
* [fix] Hide the Add Item button when filtering for trash

* [fix] Use correct bulk actions for trash filter
2022-05-13 15:17:19 -04:00
Addison Beck
7bbe3d7b28 [fix] Add max width to vault filter buttons to keep content unified (#1678) 2022-05-13 15:17:09 -04:00
Addison Beck
0564b52f1f [fix] Check policies when reloading organizations for filters (#1677) 2022-05-13 15:16:58 -04:00
Oscar Hinton
cb1f08d74b [EC-200] Handle an edge case where ciphers were not selectable (#1674)
(cherry picked from commit be30d47038)
2022-05-13 15:33:33 +02:00
Thomas Rittson
f3bf972d26 Revert accidental cnesting of org routes (#1670) 2022-05-13 08:32:26 +10:00
Oscar Hinton
02256aad87 [SG-279] Fix unlock button not working after logging in through SSO (#1672)
(cherry picked from commit f4f3e8c574)
2022-05-12 16:22:11 +02:00
Oscar Hinton
466e0c8c35 [SG-267] Exposed passwords report: console error (#1671)
(cherry picked from commit 3367736c7e)
2022-05-12 15:48:08 +02:00
Vincent Salucci
0543e76432 [bug:euvr] Self-hosted instance hiding subscription nav item (#1669) 2022-05-12 08:44:14 -04:00
Thomas Rittson
34527160fc Fix permission checking when accessing manage SSO (#1663) 2022-05-12 09:12:34 +10:00
Thomas Rittson
f590fe5435 Update Go Premium link (#1660) 2022-05-12 06:54:41 +10:00
Vincent Salucci
0f64c16c18 [bug:euvr] Change password init fix (#1667) 2022-05-11 15:44:45 -04:00
Vincent Salucci
1632a24b1e [bug:euvr] Create Organization Updates (#1664) 2022-05-11 15:44:18 -04:00
Addison Beck
0691af1dcd [fix] Update the navbar when creating an organization for the first time (#1668) 2022-05-11 14:51:05 -04:00
Robyn MacCallum
3e59617fb1 Use correct localization string (#1665) 2022-05-11 13:37:26 -04:00
Thomas Rittson
268b16ca98 [SG-234] Fix account menu icon colour (#1653) 2022-05-11 08:40:24 +10:00
Robyn MacCallum
9f977cfc68 Various small vault filter fixes (#1659) 2022-05-10 17:33:53 -04:00
Matt Gibson
9627782a04 Feature/self hosted families for enterprise (#1658)
* Remove F4E card from vault page

* Billing Sync Api Keys / Free Bitwarden Families Page updates (#1597)

* Work on billing sync key maintenance

* Work on i18n

* Work on localizations

* Work on sync status text

* Work on sync status copy

* Assign default null value

* Work on billing sync UI

* Work on sponsorships page

* Add inline form validation

* Fix lint

* Prettier

* Final touches on designs from Figma

* Polish

* Address PR feedback

* Use more generic validator

* Update jslib

* Update jslib

* Remove comment

* [PS-248] Feature/manage billing sync connection (#1601)

* Fix modal footer rounded corners

* Add billing sync messages

* Add manage billing sync to self hosted org subscription component

* Handle create vs update in component

* Update src/app/settings/billing-sync-key.component.html

Co-authored-by: Justin Baur <admin@justinbaur.com>

* update jslib

* Linter fixes

* Remove duplicate modalService

Co-authored-by: Justin Baur <admin@justinbaur.com>

* Fix/f4e self hosting section (#1616)

* Translate messages

* Move download license subform to below download license button

* Chore/merge/self hosted families for enterprise (#1617)

* [EC-142] Fix error during import of 1pux containing new email field format (#1585)

* Pull in changes made on https://github.com/bitwarden/jslib/pull/758

* Update package-lock.json

* Add descriptions to vague messages (#1586)

* Add descriptions to vague messages

* Fix typo

* Autosync the updated translations (#1590)

Co-authored-by: github-actions <>

* Bumping the pinned commit for the download-artifact to bypass the github api issue (#1598)

* Update jslib (#1602)

* Update jslib

* Update name of UserVerificationComponent

* Bumped version to 2.28.0 (#1603)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [EC-161] Bump braintree (#1606)

* [PS-211] [PS-212] Make Generator page accessible (#1607)

* Fix grouping of radiobutton inputs

* Add role=radiogroup

* Add aria-labelledBy to radio button groups

* Add reorganization notice (#1610)

* Add aria attributes to password gen options (#1611)

* [EC-143] [BEEEP] Allow linking to ciphers (#1579)

Co-authored-by: Matt Gibson <mgibson@bitwarden.com>

* update jslib

Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Joseph Flinn <58369717+joseph-flinn@users.noreply.github.com>
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>

* Chore/merge/self hosted families for enterprise (#1621)

* [EC-142] Fix error during import of 1pux containing new email field format (#1585)

* Pull in changes made on https://github.com/bitwarden/jslib/pull/758

* Update package-lock.json

* Add descriptions to vague messages (#1586)

* Add descriptions to vague messages

* Fix typo

* Autosync the updated translations (#1590)

Co-authored-by: github-actions <>

* Bumping the pinned commit for the download-artifact to bypass the github api issue (#1598)

* Update jslib (#1602)

* Update jslib

* Update name of UserVerificationComponent

* Bumped version to 2.28.0 (#1603)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [EC-161] Bump braintree (#1606)

* [PS-211] [PS-212] Make Generator page accessible (#1607)

* Fix grouping of radiobutton inputs

* Add role=radiogroup

* Add aria-labelledBy to radio button groups

* Add reorganization notice (#1610)

* Add aria attributes to password gen options (#1611)

* [EC-143] [BEEEP] Allow linking to ciphers (#1579)

Co-authored-by: Matt Gibson <mgibson@bitwarden.com>

* Fix login sponsorship redirect (#1620)

Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Joseph Flinn <58369717+joseph-flinn@users.noreply.github.com>
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>

* Change to new name (#1642)

* Chore/merge/self hosted families for enterprise (#1651)

* [EC-142] Fix error during import of 1pux containing new email field format (#1585)

* Pull in changes made on https://github.com/bitwarden/jslib/pull/758

* Update package-lock.json

* Add descriptions to vague messages (#1586)

* Add descriptions to vague messages

* Fix typo

* Autosync the updated translations (#1590)

Co-authored-by: github-actions <>

* Bumping the pinned commit for the download-artifact to bypass the github api issue (#1598)

* Update jslib (#1602)

* Update jslib

* Update name of UserVerificationComponent

* Bumped version to 2.28.0 (#1603)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [EC-161] Bump braintree (#1606)

* [PS-211] [PS-212] Make Generator page accessible (#1607)

* Fix grouping of radiobutton inputs

* Add role=radiogroup

* Add aria-labelledBy to radio button groups

* Add reorganization notice (#1610)

* Add aria attributes to password gen options (#1611)

* [EC-143] [BEEEP] Allow linking to ciphers (#1579)

Co-authored-by: Matt Gibson <mgibson@bitwarden.com>

* Fix login sponsorship redirect (#1620)

* Contribution Documentation edits (#1599)

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'

* Add description for the A-Z & a-z items (#1615)

* Add description for reports message (#1600)

Add "Vault Health Reports can be used to evaluate the security of your Bitwarden Personal or Organization Vault" description to the source string, "Identify and close security gaps in your online accounts by clicking the reports below."

* [PS-301] Load OssModule from BitwardenLicense (#1626)

* Bumped version to 2.28.1 (#1629)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [EC-154] [BEEEP] Remove factory providers in Angular DI (#1609)

* use InjectionTokens

* Use InitService

* PS-79 Updated two-factor component to to align to jslib change to send the deviceId on 2fa email resend code (#1624)

* [PS-74] Fix user authentication state checks (#1632)

* Update to use new authStatus method

* Delete unused services and import

* update jslib

* [PS-381] Fix locale being empty when not configuring a language (#1631)

* Forwarded email providers to username generator (#1628)

* forwarded emails

* firefox relay

* remove firefox relay

* update jslib ref

* remove dupe logService

* Update localization description for 'random' (#1633)

Adding description string for 'random'

* DEVOPS-758 - Move Web deploy from GitHub Pages to CloudFlare Pages (#1627)

* [EC-189] Resolve password reprompt not appearing on linkable cipher (#1643)

* [SG-220] End User Vault Refresh (#1640)

* Add premium badge component (#1525)

* [Vault Refresh] Nav update and Options -> Preferences (#1530)

* Update jslib

* [End User Vault Refresh] Security sub-page (#1538)

* [End User Vault Refresh] Security section

* Updated routing module

* Update routing for change-password

* Updated buttons of all modified classes // imported button module

* Converted modified class to use bit-callout

* removed comments

* Update small button to current cl button

* Update jslib and consequential updates

* [End User Vault Refresh] Vault - remove Org and Provider cards (#1529)

* Update reports page (#1536)

* [End User Vault Refresh] Organizations - updated nav and route permissions (#1551)

* Add Organizations link to navbar

* Update route permissions and guards

* Use NavigationPermissionsService to unify route permissions

* Rename "My Vault" to "Vaults" (#1569)

* [euvr] Adjust Vault width based on card visibility (#1588)

* [SG-31 End User Vault Refresh] Account Menu updates (#1596)

* Add menuModule

* Use bit-menu for account menu

* Fix styling, replace CSS with TW

* Change out bootstrap styling

* Fix styling

* Fix styling

* Rename My Account to Account Settings

* WIP use Avatar for account menu

* Revert "WIP use Avatar for account menu"

This reverts commit d58bea4874.

* Update jslib from feature branch

* [End User Vault Refresh] SG-16: Organization filters (#1595)

* [feature] Base implementation of EUVR filter changes

* [refactor] Relocated vault-filters to app/modules

* [refactor] Reuse vault-filters component for organizations

* [refactor] Remove unused org filter component

* [bug] .gitmodules branch change

* [bug] Load organization filters after sync during login

* [refactor] Introduce a SharedModule

* [refactor] Created a home for loose components

* [refactor] Convert VaultComponent and OrgVaultComponent into a pair of modules

* [refactor] Implement <bit-menu> for organization filter actions

* [feature] Improve a11y standards of the vault filters module

* [bug] Recreate package-lock.json

* Fix build issue

* [bug] Remove duplicate this.go() call

* [fix] Use correct filter-buttons class

Co-authored-by: addison <addisonbeck1@gmail.com>
Co-authored-by: Hinton <oscar@oscarhinton.com>

* [SG-32] Add Ownership badge to vault items (#1623)

* [feature] Base implementation of EUVR filter changes

* [refactor] Relocated vault-filters to app/modules

* [refactor] Reuse vault-filters component for organizations

* [refactor] Remove unused org filter component

* [bug] .gitmodules branch change

* [bug] Load organization filters after sync during login

* [refactor] Introduce a SharedModule

* [refactor] Created a home for loose components

* [refactor] Convert VaultComponent and OrgVaultComponent into a pair of modules

* [refactor] Implement <bit-menu> for organization filter actions

* [feature] Improve a11y standards of the vault filters module

* [bug] Recreate package-lock.json

* Fix build issue

* [bug] Remove duplicate this.go() call

* Add organization owner badge to vault items

* Fix capitalization

* Re-organize new components into modules

* Use tailwind css class

Co-authored-by: addison <addisonbeck1@gmail.com>
Co-authored-by: Hinton <oscar@oscarhinton.com>

* [EUVR] Merge master into feature branch (#1637)

* Update jslib (#1602)

* Update jslib

* Update name of UserVerificationComponent

* Bumped version to 2.28.0 (#1603)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [EC-161] Bump braintree (#1606)

* [PS-211] [PS-212] Make Generator page accessible (#1607)

* Fix grouping of radiobutton inputs

* Add role=radiogroup

* Add aria-labelledBy to radio button groups

* Add reorganization notice (#1610)

* Add aria attributes to password gen options (#1611)

* [EC-143] [BEEEP] Allow linking to ciphers (#1579)

Co-authored-by: Matt Gibson <mgibson@bitwarden.com>

* Fix login sponsorship redirect (#1620)

* Contribution Documentation edits (#1599)

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'

* Add description for the A-Z & a-z items (#1615)

* Add description for reports message (#1600)

Add "Vault Health Reports can be used to evaluate the security of your Bitwarden Personal or Organization Vault" description to the source string, "Identify and close security gaps in your online accounts by clicking the reports below."

* [PS-301] Load OssModule from BitwardenLicense (#1626)

* Bumped version to 2.28.1 (#1629)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [EC-154] [BEEEP] Remove factory providers in Angular DI (#1609)

* use InjectionTokens

* Use InitService

* PS-79 Updated two-factor component to to align to jslib change to send the deviceId on 2fa email resend code (#1624)

* [PS-74] Fix user authentication state checks (#1632)

* Update to use new authStatus method

* Delete unused services and import

* update jslib

* [PS-381] Fix locale being empty when not configuring a language (#1631)

* Forwarded email providers to username generator (#1628)

* forwarded emails

* firefox relay

* remove firefox relay

* update jslib ref

* remove dupe logService

* Update localization description for 'random' (#1633)

Adding description string for 'random'

* DEVOPS-758 - Move Web deploy from GitHub Pages to CloudFlare Pages (#1627)

* Update jslib

* Run npm i after merge with master

* Update name of UserVerificationComponent

* Fix lazy loading of routing modules

* Routing modules should have routing in their name

* Revert "Fix lazy loading of routing modules"

This reverts commit 59d4e6e06c.

* Do not eagerly load feature modules

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
Co-authored-by: dwbit <98768076+dwbit@users.noreply.github.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>

* Do not render org options menu until loaded (#1638)

* [SG-31 End User Vault Refresh] Update cipher options menu (#1593)

* Update Vault cipher option menus

* Update Send list to use same style

* [SG-207] [EUVR] Remove Organizations from Settings page (#1619)

* [fix] Cut off overflow text for link buttons (#1639)

* [SG-225] Remove BaseGuard (#1641)

* [SG-34 End User Vault Refresh] Organization Switcher (#1550)

* [euvr] Subscription/Billing updates (#1576)

* [euvr] Subscription changes

* Revert testing bang

* Removed final instance of getUserBilling

* Moved to feature/endUserVaultRefresh remote branch and updated to latest

* Removed org-billing changes

* Updated premium component header

* Updated stateservice path

* Updated billing component name

* Reverting org-billing decouple

* Using tailwind classes for CL objects

* Added TODO

* Removed divider for components within new tab nav

* Update jslib/add components to loose-components module

* Updated routing lazy load module name to match existing pattern

* Fixed bug with redirect // Added button type // Removed headers for tabbed pages

* Revert changes to .gitmodules

* [dep] Update jslib

Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
Co-authored-by: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com>
Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com>
Co-authored-by: addison <addisonbeck1@gmail.com>
Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
Co-authored-by: dwbit <98768076+dwbit@users.noreply.github.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>

* [EC-196] Move provider last in navbar (#1647)

* [euvr] Settings -> Account Settings label (#1648)

* Check for null cipher in edit (#1646)

Null ciphers signify a _new_ cipher, so no password repromt is required

* [fix] Add missing Create Org button to filters (#1650)

* update jslib

* Fix package-lock

* Fix component declarations

Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Joseph Flinn <58369717+joseph-flinn@users.noreply.github.com>
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
Co-authored-by: dwbit <98768076+dwbit@users.noreply.github.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
Co-authored-by: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com>
Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com>
Co-authored-by: addison <addisonbeck1@gmail.com>
Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com>

* Update jslib

* update jslib

Co-authored-by: Justin Baur <admin@justinbaur.com>
Co-authored-by: Justin Baur <136baur@gmail.com>
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Joseph Flinn <58369717+joseph-flinn@users.noreply.github.com>
Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
Co-authored-by: dwbit <98768076+dwbit@users.noreply.github.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
Co-authored-by: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com>
Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com>
Co-authored-by: addison <addisonbeck1@gmail.com>
Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com>
2022-05-10 16:11:59 -05:00
Addison Beck
c5877cd063 [fix] Remove an extra sync call (#1657) 2022-05-10 13:42:16 -04:00
Robyn MacCallum
136e8897ae Check for null cipher in edit (#1656) 2022-05-10 13:33:16 -04:00
Addison Beck
81c6a4b1df [fix] Override collection filter init to consider organization vault (#1652)
* [fix] Override collection filter init to consider organization vault

* [dep] Update jslib
2022-05-10 09:04:39 -04:00
Thomas Rittson
da62cec6f0 Remove testing requirements from PR template (#1654) 2022-05-10 15:03:35 +02:00
Robyn MacCallum
474df5ba5e [SG-232 & SG-251] Fix color issues with organization badge (#1649)
* Fix color issues with organization badge

* Use tokenService to get account name

* Remove unused import
2022-05-10 07:15:29 -04:00
Thomas Rittson
2c609fc6fd [EC-151] Hide Subscription/Billing information for Provider-managed organizations (#1614) 2022-05-10 17:41:52 +10:00
Addison Beck
f8a2fae82b [fix] Add missing Create Org button to filters (#1650) 2022-05-09 21:29:18 -04:00
Matt Gibson
2f04c07262 Check for null cipher in edit (#1646)
Null ciphers signify a _new_ cipher, so no password repromt is required
2022-05-09 13:53:31 -05:00
Vincent Salucci
f81195c920 [euvr] Settings -> Account Settings label (#1648) 2022-05-09 12:13:27 -04:00
Oscar Hinton
d031b53c74 [EC-196] Move provider last in navbar (#1647) 2022-05-09 18:07:31 +02:00
Thomas Rittson
468007a984 [SG-220] End User Vault Refresh (#1640)
* Add premium badge component (#1525)

* [Vault Refresh] Nav update and Options -> Preferences (#1530)

* Update jslib

* [End User Vault Refresh] Security sub-page (#1538)

* [End User Vault Refresh] Security section

* Updated routing module

* Update routing for change-password

* Updated buttons of all modified classes // imported button module

* Converted modified class to use bit-callout

* removed comments

* Update small button to current cl button

* Update jslib and consequential updates

* [End User Vault Refresh] Vault - remove Org and Provider cards (#1529)

* Update reports page (#1536)

* [End User Vault Refresh] Organizations - updated nav and route permissions (#1551)

* Add Organizations link to navbar

* Update route permissions and guards

* Use NavigationPermissionsService to unify route permissions

* Rename "My Vault" to "Vaults" (#1569)

* [euvr] Adjust Vault width based on card visibility (#1588)

* [SG-31 End User Vault Refresh] Account Menu updates (#1596)

* Add menuModule

* Use bit-menu for account menu

* Fix styling, replace CSS with TW

* Change out bootstrap styling

* Fix styling

* Fix styling

* Rename My Account to Account Settings

* WIP use Avatar for account menu

* Revert "WIP use Avatar for account menu"

This reverts commit d58bea4874.

* Update jslib from feature branch

* [End User Vault Refresh] SG-16: Organization filters (#1595)

* [feature] Base implementation of EUVR filter changes

* [refactor] Relocated vault-filters to app/modules

* [refactor] Reuse vault-filters component for organizations

* [refactor] Remove unused org filter component

* [bug] .gitmodules branch change

* [bug] Load organization filters after sync during login

* [refactor] Introduce a SharedModule

* [refactor] Created a home for loose components

* [refactor] Convert VaultComponent and OrgVaultComponent into a pair of modules

* [refactor] Implement <bit-menu> for organization filter actions

* [feature] Improve a11y standards of the vault filters module

* [bug] Recreate package-lock.json

* Fix build issue

* [bug] Remove duplicate this.go() call

* [fix] Use correct filter-buttons class

Co-authored-by: addison <addisonbeck1@gmail.com>
Co-authored-by: Hinton <oscar@oscarhinton.com>

* [SG-32] Add Ownership badge to vault items (#1623)

* [feature] Base implementation of EUVR filter changes

* [refactor] Relocated vault-filters to app/modules

* [refactor] Reuse vault-filters component for organizations

* [refactor] Remove unused org filter component

* [bug] .gitmodules branch change

* [bug] Load organization filters after sync during login

* [refactor] Introduce a SharedModule

* [refactor] Created a home for loose components

* [refactor] Convert VaultComponent and OrgVaultComponent into a pair of modules

* [refactor] Implement <bit-menu> for organization filter actions

* [feature] Improve a11y standards of the vault filters module

* [bug] Recreate package-lock.json

* Fix build issue

* [bug] Remove duplicate this.go() call

* Add organization owner badge to vault items

* Fix capitalization

* Re-organize new components into modules

* Use tailwind css class

Co-authored-by: addison <addisonbeck1@gmail.com>
Co-authored-by: Hinton <oscar@oscarhinton.com>

* [EUVR] Merge master into feature branch (#1637)

* Update jslib (#1602)

* Update jslib

* Update name of UserVerificationComponent

* Bumped version to 2.28.0 (#1603)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [EC-161] Bump braintree (#1606)

* [PS-211] [PS-212] Make Generator page accessible (#1607)

* Fix grouping of radiobutton inputs

* Add role=radiogroup

* Add aria-labelledBy to radio button groups

* Add reorganization notice (#1610)

* Add aria attributes to password gen options (#1611)

* [EC-143] [BEEEP] Allow linking to ciphers (#1579)

Co-authored-by: Matt Gibson <mgibson@bitwarden.com>

* Fix login sponsorship redirect (#1620)

* Contribution Documentation edits (#1599)

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'

* Add description for the A-Z & a-z items (#1615)

* Add description for reports message (#1600)

Add "Vault Health Reports can be used to evaluate the security of your Bitwarden Personal or Organization Vault" description to the source string, "Identify and close security gaps in your online accounts by clicking the reports below."

* [PS-301] Load OssModule from BitwardenLicense (#1626)

* Bumped version to 2.28.1 (#1629)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* [EC-154] [BEEEP] Remove factory providers in Angular DI (#1609)

* use InjectionTokens

* Use InitService

* PS-79 Updated two-factor component to to align to jslib change to send the deviceId on 2fa email resend code (#1624)

* [PS-74] Fix user authentication state checks (#1632)

* Update to use new authStatus method

* Delete unused services and import

* update jslib

* [PS-381] Fix locale being empty when not configuring a language (#1631)

* Forwarded email providers to username generator (#1628)

* forwarded emails

* firefox relay

* remove firefox relay

* update jslib ref

* remove dupe logService

* Update localization description for 'random' (#1633)

Adding description string for 'random'

* DEVOPS-758 - Move Web deploy from GitHub Pages to CloudFlare Pages (#1627)

* Update jslib

* Run npm i after merge with master

* Update name of UserVerificationComponent

* Fix lazy loading of routing modules

* Routing modules should have routing in their name

* Revert "Fix lazy loading of routing modules"

This reverts commit 59d4e6e06c.

* Do not eagerly load feature modules

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
Co-authored-by: dwbit <98768076+dwbit@users.noreply.github.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>

* Do not render org options menu until loaded (#1638)

* [SG-31 End User Vault Refresh] Update cipher options menu (#1593)

* Update Vault cipher option menus

* Update Send list to use same style

* [SG-207] [EUVR] Remove Organizations from Settings page (#1619)

* [fix] Cut off overflow text for link buttons (#1639)

* [SG-225] Remove BaseGuard (#1641)

* [SG-34 End User Vault Refresh] Organization Switcher (#1550)

* [euvr] Subscription/Billing updates (#1576)

* [euvr] Subscription changes

* Revert testing bang

* Removed final instance of getUserBilling

* Moved to feature/endUserVaultRefresh remote branch and updated to latest

* Removed org-billing changes

* Updated premium component header

* Updated stateservice path

* Updated billing component name

* Reverting org-billing decouple

* Using tailwind classes for CL objects

* Added TODO

* Removed divider for components within new tab nav

* Update jslib/add components to loose-components module

* Updated routing lazy load module name to match existing pattern

* Fixed bug with redirect // Added button type // Removed headers for tabbed pages

* Revert changes to .gitmodules

* [dep] Update jslib

Co-authored-by: Oscar Hinton <oscar@oscarhinton.com>
Co-authored-by: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com>
Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com>
Co-authored-by: addison <addisonbeck1@gmail.com>
Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
Co-authored-by: dwbit <98768076+dwbit@users.noreply.github.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com>
2022-05-09 08:21:52 -04:00
Oscar Hinton
bc054236ad [EC-189] Resolve password reprompt not appearing on linkable cipher (#1643) 2022-05-06 11:29:45 +02:00
Vince Grassia
1c31d090a3 DEVOPS-758 - Move Web deploy from GitHub Pages to CloudFlare Pages (#1627) 2022-05-03 12:21:27 -04:00
dwbit
f8d942c02c Update localization description for 'random' (#1633)
Adding description string for 'random'
2022-05-03 09:02:40 -04:00
Kyle Spearrin
248938ca00 Forwarded email providers to username generator (#1628)
* forwarded emails

* firefox relay

* remove firefox relay

* update jslib ref

* remove dupe logService
2022-05-02 10:32:15 -04:00
Oscar Hinton
06d95bb224 [PS-381] Fix locale being empty when not configuring a language (#1631) 2022-05-02 15:32:44 +02:00
Thomas Rittson
446f2027b4 [PS-74] Fix user authentication state checks (#1632)
* Update to use new authStatus method

* Delete unused services and import

* update jslib
2022-04-30 10:51:33 +10:00
Federico Maccaroni
1f0d496f21 PS-79 Updated two-factor component to to align to jslib change to send the deviceId on 2fa email resend code (#1624) 2022-04-29 13:05:05 -03:00
Thomas Rittson
2b03162bfd [EC-154] [BEEEP] Remove factory providers in Angular DI (#1609)
* use InjectionTokens

* Use InitService
2022-04-29 09:45:47 +02:00
github-actions[bot]
f586359610 Bumped version to 2.28.1 (#1629)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-04-28 11:50:04 -07:00
Matt Gibson
96641cf195 [PS-301] Load OssModule from BitwardenLicense (#1626) 2022-04-28 07:39:59 -05:00
dwbit
572758c598 Add description for reports message (#1600)
Add "Vault Health Reports can be used to evaluate the security of your Bitwarden Personal or Organization Vault" description to the source string, "Identify and close security gaps in your online accounts by clicking the reports below."
2022-04-27 17:06:50 -04:00
dwbit
df7db8ad07 Add description for the A-Z & a-z items (#1615) 2022-04-27 17:06:16 -04:00
dwbit
0439d37c14 Contribution Documentation edits (#1599)
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'
2022-04-27 17:05:30 -04:00
Matt Gibson
97f38aa654 Fix login sponsorship redirect (#1620) 2022-04-27 08:09:38 -05:00
Oscar Hinton
0444b78ad1 [EC-143] [BEEEP] Allow linking to ciphers (#1579)
Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
2022-04-25 15:41:44 +02:00
Daniel James Smith
705251fbe2 Add aria attributes to password gen options (#1611) 2022-04-22 17:58:42 +02:00
Oscar Hinton
27853481d8 Add reorganization notice (#1610) 2022-04-22 14:25:02 +02:00
Thomas Rittson
c0511f25ca [PS-211] [PS-212] Make Generator page accessible (#1607)
* Fix grouping of radiobutton inputs

* Add role=radiogroup

* Add aria-labelledBy to radio button groups
2022-04-21 09:51:00 -04:00
Oscar Hinton
1be62ac222 [EC-161] Bump braintree (#1606) 2022-04-20 20:51:33 +02:00
github-actions[bot]
8304104a7a Bumped version to 2.28.0 (#1603)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-04-19 14:14:40 -07:00
Thomas Rittson
56808a7dbb Update jslib (#1602)
* Update jslib

* Update name of UserVerificationComponent
2022-04-19 09:49:59 +02:00
Joseph Flinn
609c13faf4 Bumping the pinned commit for the download-artifact to bypass the github api issue (#1598) 2022-04-14 16:05:20 -07:00
github-actions[bot]
62b20a5c6d Autosync the updated translations (#1590)
Co-authored-by: github-actions <>
2022-04-08 11:45:22 +02:00
Matt Gibson
d56bf1211e Add descriptions to vague messages (#1586)
* Add descriptions to vague messages

* Fix typo
2022-04-07 08:20:38 -05:00
Daniel James Smith
8831f96fc2 [EC-142] Fix error during import of 1pux containing new email field format (#1585)
* Pull in changes made on https://github.com/bitwarden/jslib/pull/758

* Update package-lock.json
2022-04-06 19:10:44 +02:00
github-actions[bot]
f26dc27515 Autosync the updated translations (#1577)
Co-authored-by: github-actions <>
2022-04-01 12:29:52 +02:00
Kyle Spearrin
cb8a40d9cd generator updates (#1575)
* update generator

* update css

* add link to help article

* update jslib

* fix oss module and user type tip icon

* update jslib

* Revert "update jslib"

This reverts commit b2b13ace5e.

* revert jslib update
2022-03-31 23:32:57 -04:00
Vince Grassia
2652a2deae Add release logic for 'web-sh' image (#1573) 2022-03-30 17:21:00 -04:00
Robyn MacCallum
e1c0c9f009 update jslib and remove date pipe from routing module (#1572) 2022-03-29 22:56:02 +02:00
Oscar Hinton
612442c1bb Cherry pick premium badge and reports page (#1525, #1536) (#1571) 2022-03-29 20:55:47 +01:00
Kyle Spearrin
23b02a770a add username generation to generator (#1566)
* add username generation to generator

* move bottom buttons into existing containers
2022-03-29 15:02:48 -04:00
Robyn MacCallum
42ececbcf5 add DatePipe to providers in routing module (#1570) 2022-03-29 12:55:27 -04:00
Kyle Spearrin
11034de7d1 resolve build errors with latest jslib ref (#1565) 2022-03-25 11:00:40 -04:00
github-actions[bot]
571aaf31c4 Autosync the updated translations (#1564)
Co-authored-by: github-actions <>
2022-03-25 01:23:30 +01:00
Oscar Hinton
0884e2d761 Bump node-forge (#1562) 2022-03-24 11:54:32 +01:00
Oscar Hinton
00975e6896 Use the new KDF constants (#1559) 2022-03-24 11:15:48 +01:00
Thomas Rittson
2c43249e98 Restore order of ngModule imports (#1560) 2022-03-24 07:25:10 +10:00
Matt Gibson
575847f252 Update configurations for self-hosted (#1558)
* Update configurations for self-hosted

* Revert "Update configurations for self-hosted"

This reverts commit a1ec06c834.

* Use selfhosted.json to configure dev env
2022-03-23 13:53:41 -05:00
Oscar Hinton
d6c181c997 Define Angular CLI globals to support tree shaking (#1541) 2022-03-22 10:00:07 +01:00
Thomas Rittson
9bb004923c [JslibModule] Refactor to use JslibModule (#1556) 2022-03-21 20:40:26 +10:00
github-actions[bot]
e08726463e Autosync the updated translations (#1554)
Co-authored-by: github-actions <>
2022-03-18 01:17:26 +01:00
Justin Baur
fdf93b610c Update CSP rule for azure blobs (#1552) 2022-03-17 09:54:14 -04:00
Vince Grassia
144038ed1c Move checkout steps before setup-node steps in Build workflow (#1553) 2022-03-17 09:02:50 -04:00
Vince Grassia
5cb5e37270 Add Node caching (#1549) 2022-03-16 11:18:47 -04:00
github-actions[bot]
e266a740ba Bump version to 2.27.0 (#1545)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-03-15 15:39:35 -06:00
Chad Scharf
3b0fc94239 Update SECURITY.md (#1544)
* Update SECURITY.md

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

* Fix extra space (prettier)

* Revise language on SECURITY.md
2022-03-15 15:45:05 -04:00
Matt Gibson
32e27b5f08 Update jslib (#1542) 2022-03-15 08:11:47 -05:00
Thomas Rittson
317c40386f Update jslib (#1540) 2022-03-15 15:14:40 +10:00
github-actions[bot]
c9eeca7def Autosync the updated translations (#1537)
Co-authored-by: github-actions <>
2022-03-11 02:31:15 +01:00
Thomas Rittson
902c568c09 Remove undefined template variable (#1534) 2022-03-11 07:20:03 +10:00
Joseph Flinn
153870693b Update hotfix release branch name to hotfix-rc (#1532) 2022-03-09 12:46:20 -08:00
Addison Beck
a8cd2a6cf7 [bug] Partially revert #1516 (#1531) 2022-03-09 11:16:53 -05:00
Vince Grassia
7404da9b3c Add web-sh repository to QA container registry (#1528) 2022-03-08 16:39:54 -05:00
Oscar Hinton
9b40ce1024 Component Library Scaffolding (#1407) 2022-03-08 18:18:03 +01:00
Daniel James Smith
80ffa965e1 Pull jslib for https://github.com/bitwarden/jslib/pull/714 (#1526) 2022-03-08 13:52:56 +01:00
Oscar Hinton
57f1a5e380 Remove msbuild from workflow (#1522) 2022-03-08 11:48:12 +01:00
Daniel James Smith
18f1929f65 Pull in Dashlane and Myki importer (#1523)
* Pull jslib

* Update copy for Dashlane json and csv importers
2022-03-08 09:01:19 +10:00
Micaiah Martin
5cb3941190 BEEEP - organize linting in CI (#1520) 2022-03-07 11:02:23 -05:00
Daniel James Smith
0e515bc6c1 Remove 2fa.directory/v2 from CSP (#1519) 2022-03-04 19:04:49 +01:00
Addison Beck
e103ddf02f [feature] Refine content of the organization delete request confirmation warning (#1508)
This commit updates the organization delete request confirmation warning based on new copy from the product team.

Changes are as follows:
* Add a load toggle to the organization delete modal, as we now have data to collect.
* Adjust how the families for enterprise error state for invalid sponserships connects with the organization delete component. Previously it just sent in a localization key to use for the description, but this commit adds a union type for identifying different delete flows and moves the FOE description localization key into the template with a condition.
* Move the callout on the organization delete component to above the description text.
* Adjust content of the typical organization delete request description based on copy from the product team.
  * This includes a list of item types in use by the organization that will be deleted and the amount of each type that exist in the organization.
2022-03-04 15:03:48 +01:00
github-actions[bot]
8242989b9d Autosync the updated translations (#1518)
Co-authored-by: github-actions <>
2022-03-04 01:25:14 +01:00
Vincent Salucci
5e7d94efb8 [Captcha] Implement captcha for 2fa (#1513)
* [Captcha] Implement captcha for 2fa

* Removed center justified captcha for now
2022-03-03 18:20:43 -06:00
Addison Beck
3bc8955dd5 [dep] Update jslib (#1517) 2022-03-03 20:14:19 +01:00
Vince Grassia
bc05d27082 Add logic for pushing latest image to QA container registry (#1515) 2022-03-03 14:00:41 -05:00
Addison Beck
e93c155885 [bug] Set full width on login (#1516) 2022-03-03 12:17:19 -05:00
Daniel James Smith
1076749635 Add importing of 1passwords 1pux files (#1507)
* Pull in jslib

* Install jszip

* Display help on selecting 1pux importer

* Unzip 1pux and pass content of export.data to 1pux importer

* Update jslib
2022-03-03 15:41:48 +01:00
Thomas Rittson
06e1af6d48 Improve SSO Config validation (#1332)
* Break form controls up into reusable components

* Add proper form styling, validation, inline error messages, etc

* Move control options into class instead of template

* Add accessibility
2022-03-03 11:08:41 +01:00
github-actions[bot]
cf9a90d10e Bumped version to 2.26.2 (#1511)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-03-02 15:08:58 -06:00
Matt Gibson
6e8c15bccd Fix mobile + url encoding issue (#1510) 2022-03-02 14:49:35 -06:00
Matt Gibson
7d018e4b59 Fix dev server connector headers (#1509) 2022-03-02 14:27:34 -06:00
Chad Scharf
f832cb4138 Remove artifact binding from SSO config UI (#1502) 2022-02-28 13:43:31 -05:00
Addison Beck
b8a23cf014 [feature] Implement scope warning for exports (#1504) 2022-02-28 11:37:02 -05:00
Daniel James Smith
d0c0e80b6c BEEEP: Add missing languages (#1501)
* Pull in jslib

* Import and register missing locales

* Update supportedTranslationLocales
2022-02-25 20:59:07 +01:00
Thomas Rittson
98fb71fcb6 Update jslib (#1494)
* Update jslib

* Add i18n error message

* Update imports
2022-02-25 10:19:37 -05:00
Micaiah Martin
1b52b5a98a Added dry run logic (#1497) 2022-02-25 09:07:54 -05:00
github-actions[bot]
c3e5c74253 Autosync the updated translations (#1500)
Co-authored-by: github-actions <>
2022-02-25 12:27:49 +01:00
Micaiah Martin
df5b175cdf Remove workflows from build trigger (#1496) 2022-02-24 08:42:14 -06:00
Oscar Hinton
1c495e87c9 Add eslint (#1437) 2022-02-24 12:10:07 +01:00
Oscar Hinton
01f128a4a9 Remove dotnet restore (#1493) 2022-02-22 16:22:45 +01:00
Oscar Hinton
a4d5b145ac Exclude connectors from CSP rules (#1489) 2022-02-22 15:46:59 +01:00
Chad Scharf
d944e0e25c We're Hiring (#1492)
Added link to README.md for Bitwarden Careers page.
2022-02-22 14:04:06 +01:00
Vincent Salucci
d141ccca52 [Icons Bug] fa-remove -> bwi-close (#1487) 2022-02-20 17:03:58 -06:00
Micaiah Martin
9e872bed2c Create initial workflow (#1482) 2022-02-18 13:28:53 -06:00
github-actions[bot]
c071b692f2 Autosync the updated translations (#1486)
Co-authored-by: github-actions <>
2022-02-18 10:24:33 +01:00
Matt Gibson
041bb1bf0a Enforce Hold label (#1478)
* Enforce Hold label

* Linting

Co-authored-by: Micaiah Martin <77340197+mimartin12@users.noreply.github.com>
2022-02-16 08:43:56 -06:00
Thomas Rittson
0b5e1eb256 Exclude jslib from prettier hook (#1458)
* Exclude jslib from prettier hook
2022-02-16 07:41:41 +10:00
Addison Beck
8c39fdb21e [lib] Update jslib (#1479) 2022-02-15 15:05:49 -05:00
Addison Beck
ca3efc8fee [bug] Disable state cache (#1477) 2022-02-14 12:26:22 -05:00
github-actions[bot]
c323f38f16 Autosync the updated translations (#1461)
Co-authored-by: github-actions <>
2022-02-11 22:48:28 +01:00
Addison Beck
9df4eb4c0d [bug] Store last sync in memory (#1471) 2022-02-11 03:39:48 -05:00
Addison Beck
1712ed53be [bug] Store vault data in memory (#1470) 2022-02-11 09:21:51 +01:00
github-actions[bot]
45a39f6200 Bumped version to 2.26.1 (#1468)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-10 21:51:45 -08:00
Thomas Rittson
a2d241263b Update jslib (#1466) 2022-02-11 00:30:37 -05:00
Thomas Rittson
5987d3deda Update jslib (#1465) 2022-02-11 14:58:33 +10:00
Thomas Rittson
080a3c655e Update jslib (#1464) 2022-02-11 14:30:07 +10:00
Justin Baur
dac48242b7 Update jslib (#1462) 2022-02-10 22:16:44 -05:00
Robyn MacCallum
e4d9ab52a0 update jslib (#1460)
* update jslib

* Remove call to tokenService.clear() from logout
2022-02-10 19:51:36 -05:00
Thomas Rittson
aee8a2661e Fix Copy Verification Code not showing after first log in (#1459)
* Move init logic to load()
2022-02-11 09:48:54 +10:00
Robyn MacCallum
ff6bb236c0 Update jslib (#1457) 2022-02-10 14:22:44 -05:00
Vincent Salucci
f79b20294a [Help] Update links to new pattern (#1454)
* [Help] Update links to new pattern

* Close help

* Update jslib
2022-02-08 17:44:47 -06:00
github-actions[bot]
3a0c34b934 Bump version to 2.26.0 (#1452)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-08 15:54:35 -07:00
github-actions[bot]
e09df347f4 Bump version to 1.26.0 (#1450)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-02-08 14:46:12 -07:00
Oscar Hinton
e68ab0031d Client & Version headers (#1434) 2022-02-08 13:22:31 +01:00
Justin Baur
64416c9406 Send in error message (#1449) 2022-02-07 16:15:49 -05:00
Matt Gibson
6779adb064 Handle password protected import export (#1448)
This updates requirements without implementing any way for the UI to
interact with the new feature
2022-02-07 14:15:22 -06:00
Thomas Rittson
1b28a4b954 Update client for authService refactor (#1387)
Co-authored-by: Hinton <oscar@oscarhinton.com>
2022-02-07 20:24:49 +01:00
Addison Beck
6320498fb3 [chore] Update jslib (#1447) 2022-02-07 12:04:51 -05:00
Oscar Hinton
bfd5f3e564 Fix register button using wrong icon (#1446) 2022-02-07 12:28:28 +01:00
Thomas Rittson
c755443735 Make husky pre-commit hook executable (#1432) 2022-02-07 12:18:33 +10:00
Justin Baur
0e5f2530a9 Switch option values to be a number (#1444) 2022-02-04 16:59:20 -05:00
Vincent Salucci
5105633fa4 [Icons] Fix button icon/text margins (#1443) 2022-02-04 13:49:44 -06:00
github-actions[bot]
e975056c21 Autosync the updated translations (#1441)
Co-authored-by: github-actions <>
2022-02-04 12:48:56 +01:00
Daniel James Smith
be21167ef8 Pull jslib for https://github.com/bitwarden/jslib/pull/654 (#1439) 2022-02-03 23:44:28 +01:00
Addison Beck
e09898e4d8 Update jslib (#1436) 2022-02-03 13:54:15 -05:00
Vincent Salucci
868d235faa [Icons] FF - requested icon changes (#1435)
* [Icons] Removed FA

* Icon changes // Webpack correction // Padding updates
2022-02-03 10:20:31 -06:00
Oscar Hinton
5c764a95f4 Add CSP for development (#1431) 2022-02-03 10:17:33 +01:00
Jake Fink
596c3e86e9 Master password policy is not checked when accepting invite from an existing account (#1371)
* validate password against org policy and create update-password component

* linting and prettier

* [bug] Default rememberEmail to true (#1429)

* switching the dashes to underscores for the branch name (#1433)

(cherry picked from commit 8910430dfb)

* fix merge conflicts

* Update src/app/accounts/update-password.component.html

Co-authored-by: Justin Baur <admin@justinbaur.com>

* Update src/locales/en/messages.json

Co-authored-by: Justin Baur <admin@justinbaur.com>

* update jslib

* prettier

Co-authored-by: Addison Beck <abeck@bitwarden.com>
Co-authored-by: Joseph Flinn <58369717+joseph-flinn@users.noreply.github.com>
Co-authored-by: Justin Baur <admin@justinbaur.com>
2022-02-03 00:00:57 -05:00
Addison Beck
8030da2ed5 [bug] Default rememberEmail to true (#1429) 2022-02-02 11:20:15 -05:00
Joseph Flinn
8910430dfb switching the dashes to underscores for the branch name (#1433) 2022-02-02 07:30:19 -08:00
Joseph Flinn
6bf6d4b47f Reverting tis change for next release (#1430) 2022-02-02 06:22:25 -08:00
Oscar Hinton
ca199a398e Fix captcha and webauthn-fallback not using theme_light (#1427) 2022-02-01 15:46:16 +01:00
Chad Scharf
61ab2fbda3 Hide unsolicited SSO option in config (#1425) 2022-01-31 17:43:56 -05:00
Addison Beck
d79f074825 [bug] Extend GlobalState to supply correct default theme (#1422)
* [bug] Extend GlobalState to supply correct default theme

The default theme for most clients is System, but web uses Light.
We need to extend GlobalState in web to reflect this.

* [chore] Update jslib
2022-01-31 14:53:55 -05:00
Oscar Hinton
e3b962a779 Apply prettier on help pr (#1423) 2022-01-31 20:31:50 +01:00
DanHillesheim
cc657eb853 Update help site URLs (#1409) 2022-01-31 20:11:27 +01:00
DanHillesheim
e14a266ee0 Registration page updates (#1390)
Co-authored-by: Hinton <oscar@oscarhinton.com>
2022-01-31 20:10:35 +01:00
Thomas Rittson
e1732cfa10 Fix various bugs in Options page (#1418)
* Remove duplicate message in toast

* Set starting variables properly
2022-01-31 09:11:25 -05:00
431 changed files with 27827 additions and 12056 deletions

8
.eslintignore Normal file
View File

@@ -0,0 +1,8 @@
**/dist
**/build
jslib
webpack.config.js
scripts/optimize.js
config.js
**/node_modules

31
.eslintrc.json Normal file
View File

@@ -0,0 +1,31 @@
{
"root": true,
"env": {
"browser": 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"]
}
]
}
}

View File

@@ -21,10 +21,6 @@
<!--Required for any UI changes. Delete if not applicable-->
## Testing requirements
<!--What functionality requires testing by QA? This includes testing new behavior and regression testing-->
## Before you submit
- [ ] I have checked for **linting** errors (`npm run lint`) (required)

View File

@@ -11,6 +11,10 @@ on:
branches-ignore:
- "l10n_master"
- "gh-pages"
- "deploy"
paths-ignore:
- '.github/workflows/**'
jobs:
cloc:
@@ -28,6 +32,28 @@ jobs:
- name: Print lines of code
run: cloc --include-lang TypeScript,JavaScript,HTML,Sass,CSS --vcs git
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@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
with:
path: "~/.npm"
key: ${{ runner.os }}-npm-lint-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
setup:
name: Setup
runs-on: ubuntu-20.04
@@ -41,24 +67,25 @@ jobs:
id: version
run: echo "::set-output name=value::${GITHUB_SHA:0:7}"
build-oss-selfhost:
name: Build OSS zip
runs-on: ubuntu-20.04
needs: setup
needs:
- setup
- lint
env:
_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
with:
node-version: "16"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
- name: Set up Node
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
path: "~/.npm"
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: "16"
- name: Print environment
run: |
@@ -70,9 +97,6 @@ jobs:
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Install dependencies
run: npm ci
@@ -88,24 +112,25 @@ jobs:
path: ./web-${{ env._VERSION }}-selfhosted-open-source.zip
if-no-files-found: error
build-cloud:
name: Build Cloud zip
runs-on: ubuntu-20.04
needs: setup
needs:
- setup
- lint
env:
_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
with:
node-version: "16"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
- name: Set up Node
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
path: "~/.npm"
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: "16"
- name: Print environment
run: |
@@ -117,9 +142,6 @@ jobs:
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Install dependencies
run: npm ci
@@ -135,24 +157,25 @@ jobs:
path: ./web-${{ env._VERSION }}-cloud-COMMERCIAL.zip
if-no-files-found: error
build-commercial-selfhost:
name: Build SelfHost Docker image
runs-on: ubuntu-20.04
needs: setup
needs:
- setup
- lint
env:
_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
with:
node-version: "16"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
- name: Set up Node
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
path: "~/.npm"
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: "16"
- name: Print environment
run: |
@@ -165,19 +188,13 @@ jobs:
echo "GitHub event: $GITHUB_EVENT"
- name: Setup DCT
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
id: setup-dct
uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff
with:
azure-creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
azure-keyvault-name: "bitwarden-prod-kv"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Restore
run: dotnet tool restore
- name: Install dependencies
run: npm ci
@@ -212,11 +229,11 @@ jobs:
run: docker tag bitwarden/web bitwarden/web:dev
- name: Tag hotfix branch
if: github.ref == 'refs/heads/hotfix'
run: docker tag bitwarden/web bitwarden/web:hotfix
if: github.ref == 'refs/heads/hotfix-rc'
run: docker tag bitwarden/web bitwarden/web:hotfix-rc
- name: List Docker images
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
run: docker images
- name: Push rc image
@@ -234,31 +251,58 @@ jobs:
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
- name: Push hotfix image
if: github.ref == 'refs/heads/hotfix'
run: docker push bitwarden/web:hotfix
if: github.ref == 'refs/heads/hotfix-rc'
run: docker push bitwarden/web:hotfix-rc
env:
DOCKER_CONTENT_TRUST: 1
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
- name: Log out of Docker
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
run: |
docker logout
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
- name: Login to Azure - QA Subscription
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
with:
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
- name: Login to Azure ACR
run: az acr login -n bitwardenqa
- name: Tag and Push RC to Azure ACR QA registry
env:
REGISTRY: bitwardenqa.azurecr.io
run: |
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
if [[ "$IMAGE_TAG" == "master" ]]; then
IMAGE_TAG=dev
fi
docker tag bitwarden/web \
$REGISTRY/web-sh:$IMAGE_TAG
docker push $REGISTRY/web-sh:$IMAGE_TAG
- name: Log out of Docker
run: docker logout
build-qa:
name: Build Docker images for QA environment
runs-on: ubuntu-20.04
needs:
- setup
- lint
steps:
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
with:
node-version: "16"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
- name: Set up Node
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
path: "~/.npm"
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: "16"
- name: Print environment
run: |
@@ -278,12 +322,6 @@ jobs:
- name: Log into container registry
run: az acr login -n bitwardenqa
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Restore
run: dotnet tool restore
- name: Install dependencies
run: npm ci
@@ -339,35 +377,29 @@ jobs:
- name: Log out of Docker
run: docker logout
windows:
name: Test code on Windows
runs-on: windows-2019
steps:
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Set up NuGet
uses: nuget/setup-nuget@04b0c2b8d1b97922f67eca497d7cf0bf17b8ffe1
with:
nuget-version: "latest"
- name: Set up MSBuild
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d
- name: Cache npm
id: npm-cache
uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6
with:
path: "~/.npm"
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Set up Node
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: "16"
- name: Print environment
run: |
nuget help | grep Version
msbuild -version
dotnet --info
node --version
npm --version
echo "GitHub ref: $GITHUB_REF"
@@ -376,18 +408,13 @@ jobs:
GITHUB_REF: ${{ github.ref }}
GITHUB_EVENT: ${{ github.event_name }}
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: NPM build
run: npm run build:bit:cloud
crowdin-push:
name: Crowdin Push
if: github.ref == 'refs/heads/master'
@@ -426,6 +453,7 @@ jobs:
upload_sources: true
upload_translations: false
check-failures:
name: Check for failures
if: always()
@@ -433,6 +461,7 @@ jobs:
needs:
- cloc
- setup
- lint
- build-oss-selfhost
- build-cloud
- build-commercial-selfhost
@@ -444,6 +473,7 @@ jobs:
if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }}
env:
CLOC_STATUS: ${{ needs.cloc.result }}
LINT_STATUS: ${{ needs.lint.result }}
SETUP_STATUS: ${{ needs.setup.result }}
BUILD_OSS_SELFHOST_STATUS: ${{ needs.build-oss-selfhost.result }}
BUILD_CLOUD_STATUS: ${{ needs.build-cloud.result }}
@@ -454,6 +484,8 @@ jobs:
run: |
if [ "$CLOC_STATUS" = "failure" ]; then
exit 1
elif [ "$LINT_STATUS" = "failure" ]; then
exit 1
elif [ "$SETUP_STATUS" = "failure" ]; then
exit 1
elif [ "$BUILD_OSS_SELFHOST_STATUS" = "failure" ]; then

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,58 +12,53 @@ on:
options:
- Initial Release
- Redeploy
- Dry Run
jobs:
setup:
name: Setup
runs-on: ubuntu-20.04
outputs:
release_version: ${{ steps.version.outputs.package }}
tag_version: ${{ steps.version.outputs.tag }}
branch-name: ${{ steps.branch.outputs.branch-name }}
release_version: ${{ steps.version.outputs.version }}
tag_version: ${{ steps.version.outputs.version }}
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
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # 2.3.4
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # 2.4.0
- name: Check Release Version
id: version
run: |
version=$( jq -r ".version" package.json)
previous_release_tag_version=$(
curl -sL https://api.github.com/repos/$GITHUB_REPOSITORY/releases/latest | jq -r ".tag_name"
)
if [ "v$version" == "$previous_release_tag_version" ] && \
[ "${{ github.event.inputs.release_type }}" == "Initial Release" ]; then
echo "[!] Already released v$version. Please bump version to continue"
exit 1
fi
echo "::set-output name=package::$version"
echo "::set-output name=tag::v$version"
uses: bitwarden/gh-actions/release-version-check@ea9fab01d76940267b4147cc1c4542431246b9f6
with:
release-type: ${{ github.event.inputs.release_type }}
project-type: ts
file: package.json
- name: Get branch name
id: branch
run: |
BRANCH_NAME=$(basename ${{ github.ref }})
echo "::set-output name=branch-name::$BRANCH_NAME"
echo "::set-output name=branch_name::$BRANCH_NAME"
self-host:
name: Release self-host docker
runs-on: ubuntu-20.04
needs: setup
env:
_BRANCH_NAME: ${{ needs.setup.outputs.branch-name }}
_BRANCH_NAME: ${{ needs.setup.outputs.branch_name }}
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
_RELEASE_OPTION: ${{ github.event.inputs.release_type }}
steps:
- name: Print environment
run: |
@@ -71,7 +66,12 @@ jobs:
docker --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
echo "Github Release Option: $_RELEASE_OPTION"
- name: Checkout repo
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
########## DockerHub ##########
- name: Setup DCT
id: setup-dct
uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff
@@ -79,21 +79,25 @@ jobs:
azure-creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
azure-keyvault-name: "bitwarden-prod-kv"
- name: Checkout repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Pull latest selfhost image
run: docker pull bitwarden/web:$_BRANCH_NAME
run: |
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
docker pull bitwarden/web:latest
else
docker pull bitwarden/web:$_BRANCH_NAME
fi
- name: Tag version and latest
run: |
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
docker tag bitwarden/web:latest bitwarden/web:dryrun
else
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION
docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:latest
- name: List Docker images
run: docker images
fi
- name: Push version and latest image
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env:
DOCKER_CONTENT_TRUST: 1
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }}
@@ -101,11 +105,51 @@ jobs:
docker push bitwarden/web:$_RELEASE_VERSION
docker push bitwarden/web:latest
- name: Log out of Docker and disable Docker Notary
run: |
docker logout
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
########## ACR ##########
- name: Login to Azure - QA Subscription
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
with:
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
- name: Login to Azure ACR
run: az acr login -n bitwardenqa
- name: Tag version and latest
env:
REGISTRY: bitwardenqa.azurecr.io
run: |
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
docker tag bitwarden/web:latest $REGISTRY/web:dryrun
else
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web:$_RELEASE_VERSION
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web:latest
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web-sh:$_RELEASE_VERSION
docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web-sh:latest
fi
- name: Push version and latest image
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env:
REGISTRY: bitwardenqa.azurecr.io
run: |
docker push $REGISTRY/web:$_RELEASE_VERSION
docker push $REGISTRY/web:latest
docker push $REGISTRY/web-sh:$_RELEASE_VERSION
docker push $REGISTRY/web-sh:latest
- name: Log out of Docker
run: docker logout
ghpages-deploy:
name: Deploy Web Vault
name: Deploy Web Vault to GitHub Pages
runs-on: ubuntu-20.04
needs:
- setup
@@ -115,17 +159,17 @@ jobs:
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
steps:
- name: Checkout Repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
with:
ref: gh-pages
- name: Create deploy branch
- name: Create gh-pages-deploy branch
run: |
git switch -c deploy-$_TAG_VERSION
git push -u origin deploy-$_TAG_VERSION
git switch -c gh-pages-deploy-$_TAG_VERSION
git push -u origin gh-pages-deploy-$_TAG_VERSION
- name: Checkout Repo
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
- name: Setup git config
run: |
@@ -135,11 +179,11 @@ jobs:
git config --global url."https://".insteadOf ssh://
- name: Download latest cloud asset
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8
with:
workflow: build.yml
workflow_conclusion: success
branch: ${{ needs.setup.outputs.branch-name }}
branch: ${{ needs.setup.outputs.branch_name }}
artifacts: web-*-cloud-COMMERCIAL.zip
# This should result in a build directory in the current working directory
@@ -147,25 +191,92 @@ jobs:
run: unzip web-*-cloud-COMMERCIAL.zip
- name: Deploy GitHub Pages
uses: crazy-max/ghaction-github-pages@db4476a01402e1a7ce05f41832040eef16d14925 # v2.5.0
uses: crazy-max/ghaction-github-pages@a117e4aa1fb4854d021546d2abdfac95be568a3a # v2.6.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
target_branch: deploy-${{ needs.setup.outputs.tag_version }}
target_branch: gh-pages-deploy-${{ needs.setup.outputs.tag_version }}
build_dir: build
keep_history: true
commit_message: "Staging deploy ${{ needs.setup.outputs.release_version }}"
dry_run: ${{ github.event.inputs.release_type == 'Dry Run' }}
- name: Create Deploy PR
- name: Create GitHub Pages Deploy PR
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env:
PR_BRANCH: deploy-${{ env._TAG_VERSION }}
PR_BRANCH: gh-pages-deploy-${{ env._TAG_VERSION }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr create --title "Deploy $_RELEASE_VERSION" \
gh pr create --title "Deploy $_RELEASE_VERSION to GitHub Pages" \
--body "Deploying $_RELEASE_VERSION" \
--base gh-pages \
--head "$PR_BRANCH"
cfpages-deploy:
name: Deploy Web Vault to CloudFlare Pages branch
runs-on: ubuntu-20.04
needs:
- setup
- self-host
env:
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
steps:
- name: Checkout Repo
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
- name: Download latest cloud asset
uses: bitwarden/gh-actions/download-artifacts@c1fa8e09871a860862d6bbe36184b06d2c7e35a8
with:
workflow: build.yml
workflow_conclusion: success
branch: ${{ needs.setup.outputs.branch_name }}
artifacts: web-*-cloud-COMMERCIAL.zip
# This should result in a build directory in the current working directory
- name: Unzip build asset
run: unzip web-*-cloud-COMMERCIAL.zip
- name: Checkout Repo
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
with:
ref: deploy
path: deployment
- name: Setup git config
run: |
git config --global user.name = "GitHub Action Bot"
git config --global user.email = "<>"
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
git config --global url."https://".insteadOf ssh://
- name: Deploy CloudFlare Pages
run: |
rm -rf ./*
cp -R ../build/* .
working-directory: deployment
- name: Create cf-pages-deploy branch
run: |
git switch -c cf-pages-deploy-$_TAG_VERSION
git add .
git commit -m "Staging deploy ${{ needs.setup.outputs.release_version }}"
git push -u origin cf-pages-deploy-$_TAG_VERSION
working-directory: deployment
- name: Create CloudFlare Pages Deploy PR
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env:
PR_BRANCH: cf-pages-deploy-${{ env._TAG_VERSION }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr create --title "Deploy $_RELEASE_VERSION to CloudFlare Pages" \
--body "Deploying $_RELEASE_VERSION" \
--base deploy \
--head "$PR_BRANCH"
release:
name: Create GitHub Release
runs-on: ubuntu-20.04
@@ -173,13 +284,14 @@ jobs:
- setup
- self-host
- ghpages-deploy
- cfpages-deploy
steps:
- name: Download latest build artifacts
uses: bitwarden/gh-actions/download-artifacts@23433be15ed6fd046ce12b6889c5184a8d9c8783
with:
workflow: build.yml
workflow_conclusion: success
branch: ${{ needs.setup.outputs.branch-name }}
branch: ${{ needs.setup.outputs.branch_name }}
artifacts: "web-*-selfhosted-COMMERCIAL.zip,
web-*-selfhosted-open-source.zip"
@@ -189,7 +301,8 @@ jobs:
mv web-*-selfhosted-open-source.zip web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip
- name: Create release
uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01
with:
name: "Version ${{ needs.setup.outputs.release_version }}"
commit: ${{ github.sha }}
@@ -199,3 +312,23 @@ jobs:
web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip"
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
dry-run:
name: Dry Run Cleanup
runs-on: ubuntu-20.04
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
env:
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
needs:
- setup
- release
steps:
- name: Checkout repo
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # 2.4.0
- name: Remove gh-pages-deploy branch
run: git push origin --delete gh-pages-deploy-$_TAG_VERSION
- name: Remove cf-pages-deploy branch
run: git push origin --delete cf-pages-deploy-$_TAG_VERSION

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

@@ -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 web vault into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-web
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

@@ -1,4 +1,4 @@
FROM bitwarden/server:dev
FROM bitwarden/server
LABEL com.bitwarden.product="bitwarden"

View File

@@ -1,3 +1,9 @@
> **Repository Reorganization in Progress**
>
> We are currently migrating some projects over to a mono repository. For existing PR's we will be providing documentation on how to move/migrate them. To minimize the overhead we are actively reviewing open PRs. If possible please ensure any pending comments are resolved as soon as possible.
>
> New pull requests created during this transition period may not get addressed —if needed, please create a new PR after the reorganization is complete.
<p align="center">
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/web-vault-macbook.png" alt="" width="600" height="358" />
</p>
@@ -61,6 +67,10 @@ You can also manually adjusting your API endpoint settings by adding `config/loc
Where the `urls` object is defined by the [Urls type in jslib](https://github.com/bitwarden/jslib/blob/master/common/src/abstractions/environment.service.ts).
## 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!

View File

@@ -1,6 +1,7 @@
import { Component } from "@angular/core";
import { AppComponent as BaseAppComponent } from "src/app/app.component";
import { DisablePersonalVaultExportPolicy } from "./policies/disable-personal-vault-export.component";
import { MaximumVaultTimeoutPolicy } from "./policies/maximum-vault-timeout.component";

View File

@@ -1,11 +1,17 @@
import { DragDropModule } from "@angular/cdk/drag-drop";
import { OverlayModule } from "@angular/cdk/overlay";
import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { RouterModule } from "@angular/router";
import { InfiniteScrollModule } from "ngx-infinite-scroll";
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
import { JslibModule } from "jslib-angular/jslib.module";
import { OssRoutingModule } from "src/app/oss-routing.module";
import { OssModule } from "src/app/oss.module";
import { ServicesModule } from "src/app/services/services.module";
import { WildcardRoutingModule } from "src/app/wildcard-routing.module";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
@@ -13,35 +19,27 @@ import { OrganizationsModule } from "./organizations/organizations.module";
import { DisablePersonalVaultExportPolicyComponent } from "./policies/disable-personal-vault-export.component";
import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-timeout.component";
import { OssRoutingModule } from "src/app/oss-routing.module";
import { OssModule } from "src/app/oss.module";
import { ServicesModule } from "src/app/services/services.module";
import { WildcardRoutingModule } from "src/app/wildcard-routing.module";
@NgModule({
imports: [
OverlayModule,
OssModule,
JslibModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
ServicesModule,
BitwardenToastModule.forRoot({
maxOpened: 5,
autoDismiss: true,
closeButton: true,
}),
InfiniteScrollModule,
DragDropModule,
AppRoutingModule,
OssRoutingModule,
OrganizationsModule,
OrganizationsModule, // Must be after OssRoutingModule for competing routes to resolve properly
RouterModule,
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
],
declarations: [
AppComponent,
MaximumVaultTimeoutPolicyComponent,
DisablePersonalVaultExportPolicyComponent,
MaximumVaultTimeoutPolicyComponent,
],
bootstrap: [AppComponent],
})

View File

@@ -5,8 +5,8 @@ import "bootstrap";
import "jquery";
import "popper.js";
// tslint:disable-next-line
require("src/scss/styles.scss");
require("src/scss/tailwind.css");
import { AppModule } from "./app.module";

View File

@@ -0,0 +1,68 @@
import { Directive, Input, OnInit, Self } from "@angular/core";
import { ControlValueAccessor, FormControl, NgControl, Validators } from "@angular/forms";
import { dirtyRequired } from "jslib-angular/validators/dirty.validator";
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
@Directive()
export abstract class BaseCvaComponent implements ControlValueAccessor, OnInit {
get describedById() {
return this.showDescribedBy ? this.controlId + "Desc" : null;
}
get showDescribedBy() {
return this.helperText != null || this.controlDir.control.hasError("required");
}
get isRequired() {
return (
this.controlDir.control.hasValidator(Validators.required) ||
this.controlDir.control.hasValidator(dirtyRequired)
);
}
@Input() label: string;
@Input() controlId: string;
@Input() helperText: string;
internalControl = new FormControl("");
protected onChange: any;
protected onTouched: any;
constructor(@Self() public controlDir: NgControl) {
this.controlDir.valueAccessor = this;
}
ngOnInit() {
this.internalControl.valueChanges.subscribe(this.onValueChangesInternal);
}
onBlurInternal() {
this.onTouched();
}
// CVA interfaces
writeValue(value: string) {
this.internalControl.setValue(value);
}
registerOnChange(fn: any) {
this.onChange = fn;
}
registerOnTouched(fn: any) {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean) {
if (isDisabled) {
this.internalControl.disable();
} else {
this.internalControl.enable();
}
}
protected onValueChangesInternal: any = (value: string) => this.onChange(value);
// End CVA interfaces
}

View File

@@ -0,0 +1,16 @@
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
[attr.id]="controlId"
[attr.aria-describedby]="describedById"
[formControl]="internalControl"
(blur)="onBlurInternal()"
/>
<label class="form-check-label" [attr.for]="controlId">{{ label }}</label>
</div>
<small *ngIf="showDescribedBy" [attr.id]="describedById" class="form-text text-muted">{{
helperText
}}</small>
</div>

View File

@@ -0,0 +1,10 @@
import { Component } from "@angular/core";
import { BaseCvaComponent } from "./base-cva.component";
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
@Component({
selector: "app-input-checkbox",
templateUrl: "input-checkbox.component.html",
})
export class InputCheckboxComponent extends BaseCvaComponent {}

View File

@@ -0,0 +1,26 @@
<div class="form-group">
<label>{{ label }}</label>
<div class="input-group">
<input class="form-control" readonly [value]="controlValue" />
<div class="input-group-append" *ngIf="showLaunch">
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'launch' | i18n }}"
(click)="launchUri(controlValue)"
>
<i class="bwi bwi-lg bwi-external-link" aria-hidden="true"></i>
</button>
</div>
<div class="input-group-append" *ngIf="showCopy">
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'copyValue' | i18n }}"
(click)="copy(controlValue)"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { Component, Input } from "@angular/core";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
@Component({
selector: "app-input-text-readonly",
templateUrl: "input-text-readonly.component.html",
})
export class InputTextReadOnlyComponent {
@Input() controlValue: string;
@Input() label: string;
@Input() showCopy = true;
@Input() showLaunch = false;
constructor(private platformUtilsService: PlatformUtilsService) {}
copy(value: string) {
this.platformUtilsService.copyToClipboard(value);
}
launchUri(url: string) {
this.platformUtilsService.launchUri(url);
}
}

View File

@@ -0,0 +1,33 @@
<div class="form-group">
<label [attr.for]="controlId">
{{ label }}
<small *ngIf="isRequired" class="text-muted form-text d-inline"
>({{ "required" | i18n }})</small
>
</label>
<input
[formControl]="internalControl"
class="form-control"
[attr.id]="controlId"
[attr.aria-describedby]="describedById"
[attr.aria-invalid]="controlDir.control.invalid"
(blur)="onBlurInternal()"
/>
<div *ngIf="showDescribedBy" [attr.id]="describedById">
<small
*ngIf="helperText != null && !controlDir.control.hasError(helperTextSameAsError)"
class="form-text text-muted"
>
{{ helperText }}
</small>
<small class="error-inline" *ngIf="controlDir.control.hasError('required')" role="alert">
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
<span class="sr-only">{{ "error" | i18n }}:</span>
{{
controlDir.control.hasError(helperTextSameAsError)
? helperText
: ("fieldRequiredError" | i18n: label)
}}
</small>
</div>
</div>

View File

@@ -0,0 +1,48 @@
import { Component, Input, OnInit } from "@angular/core";
import { BaseCvaComponent } from "./base-cva.component";
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
@Component({
selector: "app-input-text[label][controlId]",
templateUrl: "input-text.component.html",
})
export class InputTextComponent extends BaseCvaComponent implements OnInit {
@Input() helperTextSameAsError: string;
@Input() requiredErrorMessage: string;
@Input() stripSpaces = false;
transformValue: (value: string) => string = null;
ngOnInit() {
super.ngOnInit();
if (this.stripSpaces) {
this.transformValue = this.doStripSpaces;
}
}
writeValue(value: string) {
this.internalControl.setValue(value == null ? "" : value);
}
protected onValueChangesInternal: any = (value: string) => {
let newValue = value;
if (this.transformValue != null) {
newValue = this.transformValue(value);
this.internalControl.setValue(newValue, { emitEvent: false });
}
this.onChange(newValue);
};
protected onValueChangeInternal(value: string) {
let newValue = value;
if (this.transformValue != null) {
newValue = this.transformValue(value);
this.internalControl.setValue(newValue, { emitEvent: false });
}
}
private doStripSpaces(value: string) {
return value.replace(/ /g, "");
}
}

View File

@@ -0,0 +1,19 @@
<div class="form-group">
<label [attr.for]="controlId">
{{ label }}
<small *ngIf="isRequired" class="text-muted form-text d-inline"
>({{ "required" | i18n }})</small
>
</label>
<select
class="form-control"
[attr.id]="controlId"
[attr.aria-invalid]="controlDir.control.invalid"
[formControl]="internalControl"
(blur)="onBlurInternal()"
>
<option *ngFor="let o of selectOptions" [ngValue]="o.value" disabled="{{ o.disabled }}">
{{ o.name }}
</option>
</select>
</div>

View File

@@ -0,0 +1,14 @@
import { Component, Input } from "@angular/core";
import { SelectOptions } from "jslib-angular/interfaces/selectOptions";
import { BaseCvaComponent } from "./base-cva.component";
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
@Component({
selector: "app-select",
templateUrl: "select.component.html",
})
export class SelectComponent extends BaseCvaComponent {
@Input() selectOptions: SelectOptions[];
}

View File

@@ -14,10 +14,9 @@
<form
#form
(ngSubmit)="submit()"
[formGroup]="data"
[formGroup]="ssoConfigForm"
[appApiAction]="formPromise"
*ngIf="!loading"
ngNativeValidate
>
<p>
{{ "ssoPolicyHelpStart" | i18n }}
@@ -27,19 +26,14 @@
{{ "ssoPolicyHelpKeyConnector" | i18n }}
</p>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="enabled"
<!-- Root form -->
<ng-container>
<app-input-checkbox
controlId="enabled"
[formControl]="enabled"
name="Enabled"
/>
<label class="form-check-label" for="enabled">{{ "allowSso" | i18n }}</label>
</div>
<small class="form-text text-muted">{{ "allowSsoDesc" | i18n }}</small>
</div>
[label]="'allowSso' | i18n"
[helperText]="'allowSsoDesc' | i18n"
></app-input-checkbox>
<div class="form-group">
<label>{{ "memberDecryptionOption" | i18n }}</label>
@@ -71,7 +65,7 @@
target="_blank"
rel="noopener"
appA11yTitle="{{ 'learnMore' | i18n }}"
href="https://bitwarden.com/help/article/about-key-connector/"
href="https://bitwarden.com/help/about-key-connector/"
>
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a>
@@ -80,19 +74,26 @@
</div>
</div>
<ng-container *ngIf="data.value.keyConnectorEnabled">
<!-- Key Connector -->
<ng-container *ngIf="ssoConfigForm.get('keyConnectorEnabled').value">
<app-callout type="warning" [useAlertRole]="true">
{{ "keyConnectorWarning" | i18n }}
</app-callout>
<div class="form-group">
<label for="keyConnectorUrl">{{ "keyConnectorUrl" | i18n }}</label>
<label for="keyConnectorUrl">
{{ "keyConnectorUrl" | i18n }}
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
</label>
<div class="input-group">
<input
class="form-control"
formControlName="keyConnectorUrl"
id="keyConnectorUrl"
required
aria-describedby="keyConnectorUrlDesc"
(change)="haveTestedKeyConnector = false"
appInputStripSpaces
appA11yInvalid
/>
<div class="input-group-append">
<button
@@ -113,375 +114,319 @@
</button>
</div>
</div>
<ng-container *ngIf="keyConnectorUrl.pristine && !keyConnectorUrl.pending">
<div class="text-danger" *ngIf="keyConnectorUrl.hasError('invalidUrl')" role="alert">
<div *ngIf="haveTestedKeyConnector" id="keyConnectorUrlDesc" aria-live="polite">
<small
class="error-inline"
*ngIf="keyConnectorUrl.hasError('invalidUrl'); else keyConnectorSuccess"
>
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
<span class="sr-only">{{ "error" | i18n }}:</span>
{{ "keyConnectorTestFail" | i18n }}
</div>
<div class="text-success" *ngIf="!keyConnectorUrl.hasError('invalidUrl')" role="alert">
</small>
<ng-template #keyConnectorSuccess>
<small class="text-success">
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
{{ "keyConnectorTestSuccess" | i18n }}
</small>
</ng-template>
</div>
</ng-container>
</div>
</ng-container>
<div class="form-group">
<label for="type">{{ "type" | i18n }}</label>
<select class="form-control" id="type" formControlName="configType">
<option [ngValue]="0" disabled>{{ "selectType" | i18n }}</option>
<option [ngValue]="1">OpenID Connect</option>
<option [ngValue]="2">SAML 2.0</option>
</select>
</div>
<app-select
controlId="type"
[label]="'type' | i18n"
[selectOptions]="ssoTypeOptions"
formControlName="configType"
>
</app-select>
</ng-container>
<!-- OIDC -->
<div *ngIf="data.value.configType == 1">
<div
*ngIf="ssoConfigForm.get('configType').value === ssoType.OpenIdConnect"
[formGroup]="openIdForm"
>
<div class="config-section">
<h2>{{ "openIdConnectConfig" | i18n }}</h2>
<div class="form-group">
<label>{{ "callbackPath" | i18n }}</label>
<div class="input-group">
<input class="form-control" readonly [value]="callbackPath" />
<div class="input-group-append">
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'copyValue' | i18n }}"
(click)="copy(callbackPath)"
<h2 class="secondary-header">{{ "openIdConnectConfig" | i18n }}</h2>
<app-input-text-readonly
[label]="'callbackPath' | i18n"
[controlValue]="callbackPath"
></app-input-text-readonly>
<app-input-text-readonly
[label]="'signedOutCallbackPath' | i18n"
[controlValue]="signedOutCallbackPath"
></app-input-text-readonly>
<app-input-text
[label]="'authority' | i18n"
controlId="authority"
[stripSpaces]="true"
formControlName="authority"
></app-input-text>
<app-input-text
[label]="'clientId' | i18n"
controlId="clientId"
[stripSpaces]="true"
formControlName="clientId"
></app-input-text>
<app-input-text
[label]="'clientSecret' | i18n"
controlId="clientSecret"
[stripSpaces]="true"
formControlName="clientSecret"
></app-input-text>
<app-input-text
[label]="'metadataAddress' | i18n"
controlId="metadataAddress"
[stripSpaces]="true"
[helperText]="'openIdAuthorityRequired' | i18n"
formControlName="metadataAddress"
></app-input-text>
<app-select
controlId="redirectBehavior"
[label]="'oidcRedirectBehavior' | i18n"
[selectOptions]="connectRedirectOptions"
formControlName="redirectBehavior"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="form-group">
<label>{{ "signedOutCallbackPath" | i18n }}</label>
<div class="input-group">
<input class="form-control" readonly [value]="signedOutCallbackPath" />
<div class="input-group-append">
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'copyValue' | i18n }}"
(click)="copy(signedOutCallbackPath)"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="form-group">
<label for="authority">{{ "authority" | i18n }}</label>
<input class="form-control" formControlName="authority" id="authority" />
</div>
<div class="form-group">
<label for="clientId">{{ "clientId" | i18n }}</label>
<input class="form-control" formControlName="clientId" id="clientId" />
</div>
<div class="form-group">
<label for="clientSecret">{{ "clientSecret" | i18n }}</label>
<input class="form-control" formControlName="clientSecret" id="clientSecret" />
</div>
<div class="form-group">
<label for="metadataAddress">{{ "metadataAddress" | i18n }}</label>
<input class="form-control" formControlName="metadataAddress" id="metadataAddress" />
</div>
<div class="form-group">
<label for="redirectBehavior">{{ "oidcRedirectBehavior" | i18n }}</label>
<select class="form-control" formControlName="redirectBehavior" id="redirectBehavior">
<option [ngValue]="0">Redirect GET</option>
<option [ngValue]="1">Form POST</option>
</select>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="getClaimsFromUserInfoEndpoint"
</app-select>
<app-input-checkbox
controlId="getClaimsFromUserInfoEndpoint"
formControlName="getClaimsFromUserInfoEndpoint"
/>
<label class="form-check-label" for="getClaimsFromUserInfoEndpoint">
{{ "getClaimsFromUserInfoEndpoint" | i18n }}
</label>
[label]="'getClaimsFromUserInfoEndpoint' | i18n"
></app-input-checkbox>
<!-- Optional customizations -->
<div
class="section-header d-flex flex-row align-items-center mt-3 mb-3"
(click)="toggleOpenIdCustomizations()"
>
<h3 class="mb-0 mr-2" id="customizations-header">
{{ "openIdOptionalCustomizations" | i18n }}
</h3>
<button
class="mb-1 btn btn-link"
type="button"
appStopClick
role="button"
aria-controls="customizations"
[attr.aria-expanded]="showOpenIdCustomizations"
aria-labelledby="customizations-header"
>
<i
class="bwi"
aria-hidden="true"
[ngClass]="{
'bwi-angle-down': !showOpenIdCustomizations,
'bwi-chevron-up': showOpenIdCustomizations
}"
></i>
</button>
</div>
</div>
<div class="form-group">
<label for="additionalScopes">{{ "additionalScopes" | i18n }}</label>
<input class="form-control" formControlName="additionalScopes" id="additionalScopes" />
</div>
<div class="form-group">
<label for="additionalUserIdClaimTypes">{{ "additionalUserIdClaimTypes" | i18n }}</label>
<input
class="form-control"
<div id="customizations" [hidden]="!showOpenIdCustomizations">
<app-input-text
[label]="'additionalScopes' | i18n"
controlId="additionalScopes"
[helperText]="'separateMultipleWithComma' | i18n"
formControlName="additionalScopes"
></app-input-text>
<app-input-text
[label]="'additionalUserIdClaimTypes' | i18n"
controlId="additionalUserIdClaimTypes"
[helperText]="'separateMultipleWithComma' | i18n"
formControlName="additionalUserIdClaimTypes"
id="additionalUserIdClaimTypes"
/>
</div>
<div class="form-group">
<label for="additionalEmailClaimTypes">{{ "additionalEmailClaimTypes" | i18n }}</label>
<input
class="form-control"
></app-input-text>
<app-input-text
[label]="'additionalEmailClaimTypes' | i18n"
controlId="additionalEmailClaimTypes"
[helperText]="'separateMultipleWithComma' | i18n"
formControlName="additionalEmailClaimTypes"
id="additionalEmailClaimTypes"
/>
</div>
<div class="form-group">
<label for="additionalNameClaimTypes">{{ "additionalNameClaimTypes" | i18n }}</label>
<input
class="form-control"
></app-input-text>
<app-input-text
[label]="'additionalNameClaimTypes' | i18n"
controlId="additionalNameClaimTypes"
[helperText]="'separateMultipleWithComma' | i18n"
formControlName="additionalNameClaimTypes"
id="additionalNameClaimTypes"
/>
</div>
<div class="form-group">
<label for="acrValues">{{ "acrValues" | i18n }}</label>
<input class="form-control" formControlName="acrValues" id="acrValues" />
</div>
<div class="form-group">
<label for="expectedReturnAcrValue">{{ "expectedReturnAcrValue" | i18n }}</label>
<input
class="form-control"
></app-input-text>
<app-input-text
[label]="'acrValues' | i18n"
controlId="acrValues"
helperText="acr_values"
formControlName="acrValues"
></app-input-text>
<app-input-text
[label]="'expectedReturnAcrValue' | i18n"
controlId="expectedReturnAcrValue"
helperText="acr_validation"
formControlName="expectedReturnAcrValue"
id="expectedReturnAcrValue"
/>
></app-input-text>
</div>
</div>
</div>
<div *ngIf="data.value.configType == 2">
<!-- SAML2 SP -->
<div *ngIf="ssoConfigForm.get('configType').value === ssoType.Saml2" [formGroup]="samlForm">
<!-- SAML2 SP -->
<div class="config-section">
<h2>{{ "samlSpConfig" | i18n }}</h2>
<div class="form-group">
<label>{{ "spEntityId" | i18n }}</label>
<div class="input-group">
<input class="form-control" readonly [value]="spEntityId" />
<div class="input-group-append">
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'copyValue' | i18n }}"
(click)="copy(spEntityId)"
<h2 class="secondary-header">{{ "samlSpConfig" | i18n }}</h2>
<app-input-text-readonly
[label]="'spEntityId' | i18n"
[controlValue]="spEntityId"
></app-input-text-readonly>
<app-input-text-readonly
[label]="'spMetadataUrl' | i18n"
[controlValue]="spMetadataUrl"
[showLaunch]="true"
></app-input-text-readonly>
<app-input-text-readonly
[label]="'spAcsUrl' | i18n"
[controlValue]="spAcsUrl"
></app-input-text-readonly>
<app-select
controlId="spNameIdFormat"
[label]="'spNameIdFormat' | i18n"
[selectOptions]="saml2NameIdFormatOptions"
formControlName="spNameIdFormat"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="form-group">
<label>{{ "spMetadataUrl" | i18n }}</label>
<div class="input-group">
<input class="form-control" readonly [value]="spMetadataUrl" />
<div class="input-group-append">
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'launch' | i18n }}"
(click)="launchUri(spMetadataUrl)"
>
<i class="bwi bwi-lg bwi-external-link" aria-hidden="true"></i>
</button>
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'copyValue' | i18n }}"
(click)="copy(spMetadataUrl)"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="form-group">
<label>{{ "spAcsUrl" | i18n }}</label>
<div class="input-group">
<input class="form-control" readonly [value]="spAcsUrl" />
<div class="input-group-append">
<button
type="button"
class="btn btn-outline-secondary"
appA11yTitle="{{ 'copyValue' | i18n }}"
(click)="copy(spAcsUrl)"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="form-group">
<label for="spNameIdFormat">{{ "spNameIdFormat" | i18n }}</label>
<select class="form-control" formControlName="spNameIdFormat" id="spNameIdFormat">
<option value="0">Not Configured</option>
<option value="1">Unspecified</option>
<option value="2">Email Address</option>
<option value="3">X.509 Subject Name</option>
<option value="4">Windows Domain Qualified Name</option>
<option value="5">Kerberos Principal Name</option>
<option value="6">Entity Identifier</option>
<option value="7">Persistent</option>
<option value="8">Transient</option>
</select>
</div>
<div class="form-group">
<label for="spOutboundSigningAlgorithm">{{ "spOutboundSigningAlgorithm" | i18n }}</label>
<select
class="form-control"
</app-select>
<app-select
controlId="spOutboundSigningAlgorithm"
[label]="'spOutboundSigningAlgorithm' | i18n"
[selectOptions]="samlSigningAlgorithmOptions"
formControlName="spOutboundSigningAlgorithm"
id="spOutboundSigningAlgorithm"
>
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option>
</select>
</div>
<div class="form-group">
<label for="spSigningBehavior">{{ "spSigningBehavior" | i18n }}</label>
<select class="form-control" formControlName="spSigningBehavior" id="spSigningBehavior">
<option value="0">If IdP Wants Authn Requests Signed</option>
<option value="1">Always</option>
<option value="3">Never</option>
</select>
</div>
<div class="form-group">
<label for="spMinIncomingSigningAlgorithm">{{
"spMinIncomingSigningAlgorithm" | i18n
}}</label>
<select
class="form-control"
</app-select>
<app-select
controlId="spSigningBehavior"
[label]="'spSigningBehavior' | i18n"
[selectOptions]="saml2SigningBehaviourOptions"
formControlName="spSigningBehavior"
>
</app-select>
<app-select
controlId="spMinIncomingSigningAlgorithm"
[label]="'spMinIncomingSigningAlgorithm' | i18n"
[selectOptions]="samlSigningAlgorithmOptions"
formControlName="spMinIncomingSigningAlgorithm"
id="spMinIncomingSigningAlgorithm"
>
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option>
</select>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="spWantAssertionsSigned"
</app-select>
<app-input-checkbox
controlId="spWantAssertionsSigned"
formControlName="spWantAssertionsSigned"
/>
<label class="form-check-label" for="spWantAssertionsSigned">
{{ "spWantAssertionsSigned" | i18n }}
</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="spValidateCertificates"
[label]="'spWantAssertionsSigned' | i18n"
></app-input-checkbox>
<app-input-checkbox
controlId="spValidateCertificates"
formControlName="spValidateCertificates"
/>
<label class="form-check-label" for="spValidateCertificates">
{{ "spValidateCertificates" | i18n }}
</label>
</div>
</div>
[label]="'spValidateCertificates' | i18n"
></app-input-checkbox>
</div>
<!-- SAML2 IDP -->
<div class="config-section">
<h2>{{ "samlIdpConfig" | i18n }}</h2>
<h2 class="secondary-header">{{ "samlIdpConfig" | i18n }}</h2>
<app-input-text
[label]="'idpEntityId' | i18n"
controlId="idpEntityId"
formControlName="idpEntityId"
></app-input-text>
<app-select
controlId="idpBindingType"
[label]="'idpBindingType' | i18n"
[selectOptions]="saml2BindingTypeOptions"
formControlName="idpBindingType"
>
</app-select>
<app-input-text
[label]="'idpSingleSignOnServiceUrl' | i18n"
controlId="idpSingleSignOnServiceUrl"
[helperText]="'idpSingleSignOnServiceUrlRequired' | i18n"
[stripSpaces]="true"
formControlName="idpSingleSignOnServiceUrl"
></app-input-text>
<app-input-text
[label]="'idpSingleLogoutServiceUrl' | i18n"
controlId="idpSingleLogoutServiceUrl"
[stripSpaces]="true"
formControlName="idpSingleLogoutServiceUrl"
></app-input-text>
<div class="form-group">
<label for="idpEntityId">{{ "idpEntityId" | i18n }}</label>
<input class="form-control" formControlName="idpEntityId" id="idpEntityId" />
</div>
<div class="form-group">
<label for="idpBindingType">{{ "idpBindingType" | i18n }}</label>
<select class="form-control" formControlName="idpBindingType" id="idpBindingType">
<option value="1">Redirect</option>
<option value="2">HTTP POST</option>
<option value="4">Artifact</option>
</select>
</div>
<div class="form-group">
<label for="idpSingleSignOnServiceUrl">{{ "idpSingleSignOnServiceUrl" | i18n }}</label>
<input
class="form-control"
formControlName="idpSingleSignOnServiceUrl"
id="idpSingleSignOnServiceUrl"
/>
</div>
<div class="form-group">
<label for="idpSingleLogoutServiceUrl">{{ "idpSingleLogoutServiceUrl" | i18n }}</label>
<input
class="form-control"
formControlName="idpSingleLogoutServiceUrl"
id="idpSingleLogoutServiceUrl"
/>
</div>
<div class="form-group">
<label for="idpArtifactResolutionServiceUrl">{{
"idpArtifactResolutionServiceUrl" | i18n
}}</label>
<input
class="form-control"
formControlName="idpArtifactResolutionServiceUrl"
id="idpArtifactResolutionServiceUrl"
/>
</div>
<div class="form-group">
<label for="idpX509PublicCert">{{ "idpX509PublicCert" | i18n }}</label>
<label for="idpX509PublicCert">
{{ "idpX509PublicCert" | i18n }}
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
</label>
<textarea
formControlName="idpX509PublicCert"
class="form-control form-control-sm text-monospace"
rows="6"
id="idpX509PublicCert"
appA11yInvalid
aria-describedby="idpX509PublicCertDesc"
></textarea>
</div>
<div class="form-group">
<label for="idpOutboundSigningAlgorithm">{{ "idpOutboundSigningAlgorithm" | i18n }}</label>
<select
class="form-control"
formControlName="idpOutboundSigningAlgorithm"
id="idpOutboundSigningAlgorithm"
<small
id="idpX509PublicCertDesc"
class="error-inline"
role="alert"
*ngIf="samlForm.get('idpX509PublicCert').hasError('required')"
>
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{ o }}</option>
</select>
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
<span class="sr-only">{{ "error" | i18n }}:</span>
{{ "fieldRequiredError" | i18n: ("idpX509PublicCert" | i18n) }}
</small>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="idpAllowUnsolicitedAuthnResponse"
<app-select
controlId="idpOutboundSigningAlgorithm"
[label]="'idpOutboundSigningAlgorithm' | i18n"
[selectOptions]="samlSigningAlgorithmOptions"
formControlName="idpOutboundSigningAlgorithm"
>
</app-select>
<!--TODO: Uncomment once Unsolicited IdP Response is supported-->
<!-- <app-input-checkbox
controlId="idpAllowUnsolicitedAuthnResponse"
formControlName="idpAllowUnsolicitedAuthnResponse"
/>
<label class="form-check-label" for="idpAllowUnsolicitedAuthnResponse">
{{ "idpAllowUnsolicitedAuthnResponse" | i18n }}
</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="idpDisableOutboundLogoutRequests"
formControlName="idpDisableOutboundLogoutRequests"
/>
<label class="form-check-label" for="idpDisableOutboundLogoutRequests">
{{ "idpDisableOutboundLogoutRequests" | i18n }}
</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="idpWantAuthnRequestsSigned"
[label]="'idpAllowUnsolicitedAuthnResponse' | i18n"
></app-input-checkbox> -->
<app-input-checkbox
controlId="idpAllowOutboundLogoutRequests"
formControlName="idpAllowOutboundLogoutRequests"
[label]="'idpAllowOutboundLogoutRequests' | i18n"
></app-input-checkbox>
<app-input-checkbox
controlId="idpWantAuthnRequestsSigned"
formControlName="idpWantAuthnRequestsSigned"
/>
<label class="form-check-label" for="idpWantAuthnRequestsSigned">
{{ "idpWantAuthnRequestsSigned" | i18n }}
</label>
</div>
</div>
[label]="'idpSignAuthenticationRequests' | i18n"
></app-input-checkbox>
</div>
</div>
@@ -489,4 +434,15 @@
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span>{{ "save" | i18n }}</span>
</button>
<div
id="errorSummary"
class="error-summary text-danger"
*ngIf="this.getErrorCount(ssoConfigForm) as errorCount"
>
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
<span class="sr-only">{{ "error" | i18n }}:</span>
{{
(errorCount === 1 ? "formErrorSummarySingle" : "formErrorSummaryPlural") | i18n: errorCount
}}
</div>
</form>

View File

@@ -1,29 +1,82 @@
import { Component, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { AbstractControl, FormBuilder, FormGroup } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { SelectOptions } from "jslib-angular/interfaces/selectOptions";
import { dirtyRequired } from "jslib-angular/validators/dirty.validator";
import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import {
OpenIdConnectRedirectBehavior,
Saml2BindingType,
Saml2NameIdFormat,
Saml2SigningBehavior,
SsoType,
} from "jslib-common/enums/ssoEnums";
import { Utils } from "jslib-common/misc/utils";
import { SsoConfigApi } from "jslib-common/models/api/ssoConfigApi";
import { Organization } from "jslib-common/models/domain/organization";
import { OrganizationSsoRequest } from "jslib-common/models/request/organization/organizationSsoRequest";
import { OrganizationSsoResponse } from "jslib-common/models/response/organization/organizationSsoResponse";
import { SsoConfigView } from "jslib-common/models/view/ssoConfigView";
const defaultSigningAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
@Component({
selector: "app-org-manage-sso",
templateUrl: "sso.component.html",
})
export class SsoComponent implements OnInit {
samlSigningAlgorithms = [
readonly ssoType = SsoType;
readonly ssoTypeOptions: SelectOptions[] = [
{ name: this.i18nService.t("selectType"), value: SsoType.None, disabled: true },
{ name: "OpenID Connect", value: SsoType.OpenIdConnect },
{ name: "SAML 2.0", value: SsoType.Saml2 },
];
readonly samlSigningAlgorithms = [
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"http://www.w3.org/2000/09/xmldsig#rsa-sha384",
"http://www.w3.org/2000/09/xmldsig#rsa-sha512",
"http://www.w3.org/2000/09/xmldsig#rsa-sha1",
];
readonly saml2SigningBehaviourOptions: SelectOptions[] = [
{
name: "If IdP Wants Authn Requests Signed",
value: Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned,
},
{ name: "Always", value: Saml2SigningBehavior.Always },
{ name: "Never", value: Saml2SigningBehavior.Never },
];
readonly saml2BindingTypeOptions: SelectOptions[] = [
{ name: "Redirect", value: Saml2BindingType.HttpRedirect },
{ name: "HTTP POST", value: Saml2BindingType.HttpPost },
];
readonly saml2NameIdFormatOptions: SelectOptions[] = [
{ name: "Not Configured", value: Saml2NameIdFormat.NotConfigured },
{ name: "Unspecified", value: Saml2NameIdFormat.Unspecified },
{ name: "Email Address", value: Saml2NameIdFormat.EmailAddress },
{ name: "X.509 Subject Name", value: Saml2NameIdFormat.X509SubjectName },
{ name: "Windows Domain Qualified Name", value: Saml2NameIdFormat.WindowsDomainQualifiedName },
{ name: "Kerberos Principal Name", value: Saml2NameIdFormat.KerberosPrincipalName },
{ name: "Entity Identifier", value: Saml2NameIdFormat.EntityIdentifier },
{ name: "Persistent", value: Saml2NameIdFormat.Persistent },
{ name: "Transient", value: Saml2NameIdFormat.Transient },
];
readonly connectRedirectOptions: SelectOptions[] = [
{ name: "Redirect GET", value: OpenIdConnectRedirectBehavior.RedirectGet },
{ name: "Form POST", value: OpenIdConnectRedirectBehavior.FormPost },
];
showOpenIdCustomizations = false;
loading = true;
haveTestedKeyConnector = false;
organizationId: string;
organization: Organization;
formPromise: Promise<any>;
@@ -35,18 +88,14 @@ export class SsoComponent implements OnInit {
spAcsUrl: string;
enabled = this.formBuilder.control(false);
data = this.formBuilder.group({
configType: [],
keyConnectorEnabled: [],
keyConnectorUrl: [],
// OpenId
authority: [],
clientId: [],
clientSecret: [],
openIdForm = this.formBuilder.group(
{
authority: ["", dirtyRequired],
clientId: ["", dirtyRequired],
clientSecret: ["", dirtyRequired],
metadataAddress: [],
redirectBehavior: [],
redirectBehavior: [OpenIdConnectRedirectBehavior.RedirectGet, dirtyRequired],
getClaimsFromUserInfoEndpoint: [],
additionalScopes: [],
additionalUserIdClaimTypes: [],
@@ -54,25 +103,42 @@ export class SsoComponent implements OnInit {
additionalNameClaimTypes: [],
acrValues: [],
expectedReturnAcrValue: [],
},
{
updateOn: "blur",
}
);
// SAML
spNameIdFormat: [],
spOutboundSigningAlgorithm: [],
spSigningBehavior: [],
spMinIncomingSigningAlgorithm: [],
samlForm = this.formBuilder.group(
{
spNameIdFormat: [Saml2NameIdFormat.NotConfigured],
spOutboundSigningAlgorithm: [defaultSigningAlgorithm],
spSigningBehavior: [Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned],
spMinIncomingSigningAlgorithm: [defaultSigningAlgorithm],
spWantAssertionsSigned: [],
spValidateCertificates: [],
idpEntityId: [],
idpBindingType: [],
idpEntityId: ["", dirtyRequired],
idpBindingType: [Saml2BindingType.HttpRedirect],
idpSingleSignOnServiceUrl: [],
idpSingleLogoutServiceUrl: [],
idpArtifactResolutionServiceUrl: [],
idpX509PublicCert: [],
idpOutboundSigningAlgorithm: [],
idpX509PublicCert: ["", dirtyRequired],
idpOutboundSigningAlgorithm: [defaultSigningAlgorithm],
idpAllowUnsolicitedAuthnResponse: [],
idpDisableOutboundLogoutRequests: [],
idpAllowOutboundLogoutRequests: [true],
idpWantAuthnRequestsSigned: [],
},
{
updateOn: "blur",
}
);
ssoConfigForm = this.formBuilder.group({
configType: [SsoType.None],
keyConnectorEnabled: [false],
keyConnectorUrl: [""],
openId: this.openIdForm,
saml: this.samlForm,
});
constructor(
@@ -85,6 +151,25 @@ export class SsoComponent implements OnInit {
) {}
async ngOnInit() {
this.ssoConfigForm.get("configType").valueChanges.subscribe((newType: SsoType) => {
if (newType === SsoType.OpenIdConnect) {
this.openIdForm.enable();
this.samlForm.disable();
} else if (newType === SsoType.Saml2) {
this.openIdForm.disable();
this.samlForm.enable();
} else {
this.openIdForm.disable();
this.samlForm.disable();
}
});
this.samlForm
.get("spSigningBehavior")
.valueChanges.subscribe(() =>
this.samlForm.get("idpX509PublicCert").updateValueAndValidity()
);
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
await this.load();
@@ -94,9 +179,7 @@ export class SsoComponent implements OnInit {
async load() {
this.organization = await this.organizationService.get(this.organizationId);
const ssoSettings = await this.apiService.getOrganizationSso(this.organizationId);
this.data.patchValue(ssoSettings.data);
this.enabled.setValue(ssoSettings.enabled);
this.populateForm(ssoSettings);
this.callbackPath = ssoSettings.urls.callbackPath;
this.signedOutCallbackPath = ssoSettings.urls.signedOutCallbackPath;
@@ -104,28 +187,30 @@ export class SsoComponent implements OnInit {
this.spMetadataUrl = ssoSettings.urls.spMetadataUrl;
this.spAcsUrl = ssoSettings.urls.spAcsUrl;
this.keyConnectorUrl.markAsDirty();
this.loading = false;
}
copy(value: string) {
this.platformUtilsService.copyToClipboard(value);
}
launchUri(url: string) {
this.platformUtilsService.launchUri(url);
}
async submit() {
this.formPromise = this.postData();
this.validateForm(this.ssoConfigForm);
if (this.ssoConfigForm.get("keyConnectorEnabled").value) {
await this.validateKeyConnectorUrl();
}
if (!this.ssoConfigForm.valid) {
this.readOutErrors();
return;
}
const request = new OrganizationSsoRequest();
request.enabled = this.enabled.value;
request.data = SsoConfigApi.fromView(this.ssoConfigForm.value as SsoConfigView);
this.formPromise = this.apiService.postOrganizationSso(this.organizationId, request);
try {
const response = await this.formPromise;
this.data.patchValue(response.data);
this.enabled.setValue(response.enabled);
this.populateForm(response);
this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved"));
} catch {
// Logged by appApiAction, do nothing
@@ -134,24 +219,8 @@ export class SsoComponent implements OnInit {
this.formPromise = null;
}
async postData() {
if (this.data.get("keyConnectorEnabled").value) {
await this.validateKeyConnectorUrl();
if (this.keyConnectorUrl.hasError("invalidUrl")) {
throw new Error(this.i18nService.t("keyConnectorTestFail"));
}
}
const request = new OrganizationSsoRequest();
request.enabled = this.enabled.value;
request.data = this.data.value;
return this.apiService.postOrganizationSso(this.organizationId, request);
}
async validateKeyConnectorUrl() {
if (this.keyConnectorUrl.pristine) {
if (this.haveTestedKeyConnector) {
return;
}
@@ -166,18 +235,84 @@ export class SsoComponent implements OnInit {
});
}
this.keyConnectorUrl.markAsPristine();
this.haveTestedKeyConnector = true;
}
toggleOpenIdCustomizations() {
this.showOpenIdCustomizations = !this.showOpenIdCustomizations;
}
getErrorCount(form: FormGroup): number {
return Object.values(form.controls).reduce((acc: number, control: AbstractControl) => {
if (control instanceof FormGroup) {
return acc + this.getErrorCount(control);
}
if (control.errors == null) {
return acc;
}
return acc + Object.keys(control.errors).length;
}, 0);
}
get enableTestKeyConnector() {
return (
this.data.get("keyConnectorEnabled").value &&
this.keyConnectorUrl != null &&
this.keyConnectorUrl.value !== ""
this.ssoConfigForm.get("keyConnectorEnabled").value &&
!Utils.isNullOrWhitespace(this.keyConnectorUrl?.value)
);
}
get keyConnectorUrl() {
return this.data.get("keyConnectorUrl");
return this.ssoConfigForm.get("keyConnectorUrl");
}
get samlSigningAlgorithmOptions(): SelectOptions[] {
return this.samlSigningAlgorithms.map((algorithm) => ({ name: algorithm, value: algorithm }));
}
private validateForm(form: FormGroup) {
Object.values(form.controls).forEach((control: AbstractControl) => {
if (control.disabled) {
return;
}
if (control instanceof FormGroup) {
this.validateForm(control);
} else {
control.markAsDirty();
control.markAsTouched();
control.updateValueAndValidity();
}
});
}
private populateForm(ssoSettings: OrganizationSsoResponse) {
this.enabled.setValue(ssoSettings.enabled);
if (ssoSettings.data != null) {
const ssoConfigView = new SsoConfigView(ssoSettings.data);
this.ssoConfigForm.patchValue(ssoConfigView);
}
}
private readOutErrors() {
const errorText = this.i18nService.t("error");
const errorCount = this.getErrorCount(this.ssoConfigForm);
const errorCountText = this.i18nService.t(
errorCount === 1 ? "formErrorSummarySingle" : "formErrorSummaryPlural",
errorCount.toString()
);
const div = document.createElement("div");
div.className = "sr-only";
div.id = "srErrorCount";
div.setAttribute("aria-live", "polite");
div.innerText = errorText + ": " + errorCountText;
const existing = document.getElementById("srErrorCount");
if (existing != null) {
existing.remove();
}
document.body.append(div);
}
}

View File

@@ -1,14 +1,13 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
import { AuthGuard } from "jslib-angular/guards/auth.guard";
import { Permissions } from "jslib-common/enums/permissions";
import { OrganizationLayoutComponent } from "src/app/layouts/organization-layout.component";
import { PermissionsGuard } from "src/app/organizations/guards/permissions.guard";
import { OrganizationLayoutComponent } from "src/app/organizations/layouts/organization-layout.component";
import { ManageComponent } from "src/app/organizations/manage/manage.component";
import { OrganizationGuardService } from "src/app/services/organization-guard.service";
import { OrganizationTypeGuardService } from "src/app/services/organization-type-guard.service";
import { NavigationPermissionsService } from "src/app/organizations/services/navigation-permissions.service";
import { SsoComponent } from "./manage/sso.component";
@@ -16,25 +15,16 @@ const routes: Routes = [
{
path: "organizations/:organizationId",
component: OrganizationLayoutComponent,
canActivate: [AuthGuardService, OrganizationGuardService],
canActivate: [AuthGuard, PermissionsGuard],
children: [
{
path: "manage",
component: ManageComponent,
canActivate: [OrganizationTypeGuardService],
canActivate: [PermissionsGuard],
data: {
permissions: [
Permissions.CreateNewCollections,
Permissions.EditAnyCollection,
Permissions.DeleteAnyCollection,
Permissions.EditAssignedCollections,
Permissions.DeleteAssignedCollections,
Permissions.AccessEventLogs,
Permissions.ManageGroups,
Permissions.ManageUsers,
Permissions.ManagePolicies,
Permissions.ManageSso,
],
permissions: NavigationPermissionsService.getPermissions("manage").concat(
Permissions.ManageSso
),
},
children: [
{

View File

@@ -2,13 +2,31 @@ import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { OssModule } from "src/app/oss.module";
import { JslibModule } from "jslib-angular/jslib.module";
import { InputCheckboxComponent } from "./components/input-checkbox.component";
import { InputTextReadOnlyComponent } from "./components/input-text-readonly.component";
import { InputTextComponent } from "./components/input-text.component";
import { SelectComponent } from "./components/select.component";
import { SsoComponent } from "./manage/sso.component";
import { OrganizationsRoutingModule } from "./organizations-routing.module";
// Form components are for use in the SSO Configuration Form only and should not be exported for use elsewhere.
// They will be deprecated by the Component Library.
@NgModule({
imports: [CommonModule, FormsModule, ReactiveFormsModule, OssModule, OrganizationsRoutingModule],
declarations: [SsoComponent],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
JslibModule,
OrganizationsRoutingModule,
],
declarations: [
InputCheckboxComponent,
InputTextComponent,
InputTextReadOnlyComponent,
SelectComponent,
SsoComponent,
],
})
export class OrganizationsModule {}

View File

@@ -1,12 +1,7 @@
import { Component } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PolicyType } from "jslib-common/enums/policyType";
import { PolicyRequest } from "jslib-common/models/request/policyRequest";
import {
BasePolicy,
BasePolicyComponent,

View File

@@ -2,9 +2,7 @@ import { Component } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PolicyType } from "jslib-common/enums/policyType";
import { PolicyRequest } from "jslib-common/models/request/policyRequest";
import {

View File

@@ -1,16 +1,14 @@
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { ValidationService } from "jslib-angular/services/validation.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.service";
import { ValidationService } from "jslib-angular/services/validation.service";
import { WebProviderService } from "../services/webProvider.service";
import { Organization } from "jslib-common/models/domain/organization";
import { Provider } from "jslib-common/models/domain/provider";
import { WebProviderService } from "../services/webProvider.service";
@Component({
selector: "provider-add-organization",
templateUrl: "add-organization.component.html",

View File

@@ -1,8 +1,9 @@
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { first } from "rxjs/operators";
import { ModalService } from "jslib-angular/services/modal.service";
import { ValidationService } from "jslib-angular/services/validation.service";
import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
@@ -10,13 +11,8 @@ import { OrganizationService } from "jslib-common/abstractions/organization.serv
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.service";
import { SearchService } from "jslib-common/abstractions/search.service";
import { ModalService } from "jslib-angular/services/modal.service";
import { ValidationService } from "jslib-angular/services/validation.service";
import { PlanType } from "jslib-common/enums/planType";
import { ProviderUserType } from "jslib-common/enums/providerUserType";
import { Organization } from "jslib-common/models/domain/organization";
import { ProviderOrganizationOrganizationDetailsResponse } from "jslib-common/models/response/provider/providerOrganizationResponse";

View File

@@ -2,11 +2,10 @@ import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router";
import { ProviderService } from "jslib-common/abstractions/provider.service";
import { Permissions } from "jslib-common/enums/permissions";
@Injectable()
export class ProviderTypeGuardService implements CanActivate {
export class PermissionsGuard implements CanActivate {
constructor(private providerService: ProviderService, private router: Router) {}
async canActivate(route: ActivatedRouteSnapshot) {

View File

@@ -6,7 +6,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { ProviderService } from "jslib-common/abstractions/provider.service";
@Injectable()
export class ProviderGuardService implements CanActivate {
export class ProviderGuard implements CanActivate {
constructor(
private router: Router,
private platformUtilsService: PlatformUtilsService,

View File

@@ -24,7 +24,11 @@
<p>{{ "joinProviderDesc" | i18n }}</p>
<hr />
<div class="d-flex">
<a routerLink="/" [queryParams]="{ email: email }" class="btn btn-primary btn-block">
<a
routerLink="/login"
[queryParams]="{ email: email }"
class="btn btn-primary btn-block"
>
{{ "logIn" | i18n }}
</a>
<a

View File

@@ -1,15 +1,14 @@
import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { ProviderUserAcceptRequest } from "jslib-common/models/request/provider/providerUserAcceptRequest";
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
@Component({
selector: "app-accept-provider",
templateUrl: "accept-provider.component.html",

View File

@@ -1,10 +1,9 @@
import { Component, Input } from "@angular/core";
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
import { ProviderUserBulkConfirmRequest } from "jslib-common/models/request/provider/providerUserBulkConfirmRequest";
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
import { BulkConfirmComponent as OrganizationBulkConfirmComponent } from "src/app/organizations/manage/bulk/bulk-confirm.component";
import { BulkUserDetails } from "src/app/organizations/manage/bulk/bulk-status.component";

View File

@@ -1,27 +1,24 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
import { ApiService } from "jslib-common/abstractions/api.service";
import { ExportService } from "jslib-common/abstractions/export.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 { ProviderService } from "jslib-common/abstractions/provider.service";
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
import { EventResponse } from "jslib-common/models/response/eventResponse";
import { EventService } from "src/app/services/event.service";
import { BaseEventsComponent } from "src/app/common/base.events.component";
import { EventService } from "src/app/services/event.service";
@Component({
selector: "provider-events",
templateUrl: "events.component.html",
})
export class EventsComponent extends BaseEventsComponent implements OnInit {
exportFileName: string = "provider-events";
exportFileName = "provider-events";
providerId: string;
private providerUsersUserIdMap = new Map<string, any>();

View File

@@ -2,7 +2,6 @@ import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { ProviderService } from "jslib-common/abstractions/provider.service";
import { Provider } from "jslib-common/models/domain/provider";
@Component({

View File

@@ -214,7 +214,7 @@
{{ "eventLogs" | i18n }}
</a>
<a class="dropdown-item text-danger" href="#" appStopClick (click)="remove(u)">
<i class="bwi bwi-fw bwi-remove" aria-hidden="true"></i>
<i class="bwi bwi-fw bwi-close" aria-hidden="true"></i>
{{ "remove" | i18n }}
</a>
</div>

View File

@@ -1,8 +1,11 @@
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
import { ModalService } from "jslib-angular/services/modal.service";
import { ValidationService } from "jslib-angular/services/validation.service";
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,26 +14,18 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { ProviderService } from "jslib-common/abstractions/provider.service";
import { SearchService } from "jslib-common/abstractions/search.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { ModalService } from "jslib-angular/services/modal.service";
import { ValidationService } from "jslib-angular/services/validation.service";
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
import { ProviderUserType } from "jslib-common/enums/providerUserType";
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
import { ListResponse } from "jslib-common/models/response/listResponse";
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
import { ProviderUserBulkRequest } from "jslib-common/models/request/provider/providerUserBulkRequest";
import { ProviderUserConfirmRequest } from "jslib-common/models/request/provider/providerUserConfirmRequest";
import { ListResponse } from "jslib-common/models/response/listResponse";
import { ProviderUserBulkResponse } from "jslib-common/models/response/provider/providerUserBulkResponse";
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
import { BasePeopleComponent } from "src/app/common/base.people.component";
import { BulkStatusComponent } from "src/app/organizations/manage/bulk/bulk-status.component";
import { EntityEventsComponent } from "src/app/organizations/manage/entity-events.component";
import { BulkConfirmComponent } from "./bulk/bulk-confirm.component";
import { BulkRemoveComponent } from "./bulk/bulk-remove.component";
import { UserAddEditComponent } from "./user-add-edit.component";
@@ -158,17 +153,13 @@ export class PeopleComponent
}
async events(user: ProviderUserUserDetailsResponse) {
const [modal] = await this.modalService.openViewRef(
EntityEventsComponent,
this.eventsModalRef,
(comp) => {
await this.modalService.openViewRef(EntityEventsComponent, this.eventsModalRef, (comp) => {
comp.name = this.userNamePipe.transform(user);
comp.providerId = this.providerId;
comp.entityId = user.id;
comp.showUser = false;
comp.entity = "user";
}
);
});
}
async bulkRemove() {
@@ -272,13 +263,14 @@ export class PeopleComponent
childComponent.users = users.map((user) => {
let message = keyedErrors[user.id] ?? successfullMessage;
// eslint-disable-next-line
if (!keyedFilteredUsers.hasOwnProperty(user.id)) {
message = this.i18nService.t("bulkFilteredMessage");
}
return {
user: user,
error: keyedErrors.hasOwnProperty(user.id),
error: keyedErrors.hasOwnProperty(user.id), // eslint-disable-line
message: message,
};
});

View File

@@ -52,7 +52,7 @@
target="_blank"
rel="noopener"
appA11yTitle="{{ 'learnMore' | i18n }}"
href="https://bitwarden.com/help/article/user-types-access-control/#user-types"
href="https://bitwarden.com/help/provider-users/"
>
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a>

View File

@@ -4,12 +4,9 @@ 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 { ProviderUserInviteRequest } from "jslib-common/models/request/provider/providerUserInviteRequest";
import { PermissionsApi } from "jslib-common/models/api/permissionsApi";
import { ProviderUserType } from "jslib-common/enums/providerUserType";
import { PermissionsApi } from "jslib-common/models/api/permissionsApi";
import { ProviderUserInviteRequest } from "jslib-common/models/request/provider/providerUserInviteRequest";
import { ProviderUserUpdateRequest } from "jslib-common/models/request/provider/providerUserUpdateRequest";
@Component({
@@ -24,7 +21,7 @@ export class UserAddEditComponent implements OnInit {
@Output() onDeletedUser = new EventEmitter();
loading = true;
editMode: boolean = false;
editMode = false;
title: string;
emails: string;
type: ProviderUserType = ProviderUserType.ServiceUser;

View File

@@ -2,7 +2,6 @@ import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { ProviderService } from "jslib-common/abstractions/provider.service";
import { Provider } from "jslib-common/models/domain/provider";
@Component({

View File

@@ -1,32 +1,30 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
import { AuthGuard } from "jslib-angular/guards/auth.guard";
import { Permissions } from "jslib-common/enums/permissions";
import { AddOrganizationComponent } from "./clients/add-organization.component";
import { FrontendLayoutComponent } from "src/app/layouts/frontend-layout.component";
import { ProvidersComponent } from "src/app/providers/providers.component";
import { ClientsComponent } from "./clients/clients.component";
import { CreateOrganizationComponent } from "./clients/create-organization.component";
import { PermissionsGuard } from "./guards/provider-type.guard";
import { ProviderGuard } from "./guards/provider.guard";
import { AcceptProviderComponent } from "./manage/accept-provider.component";
import { EventsComponent } from "./manage/events.component";
import { ManageComponent } from "./manage/manage.component";
import { PeopleComponent } from "./manage/people.component";
import { ProvidersLayoutComponent } from "./providers-layout.component";
import { AccountComponent } from "./settings/account.component";
import { SettingsComponent } from "./settings/settings.component";
import { SetupProviderComponent } from "./setup/setup-provider.component";
import { SetupComponent } from "./setup/setup.component";
import { FrontendLayoutComponent } from "src/app/layouts/frontend-layout.component";
import { ProvidersComponent } from "src/app/providers/providers.component";
import { ProviderGuardService } from "./services/provider-guard.service";
import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
import { AccountComponent } from "./settings/account.component";
const routes: Routes = [
{
path: "",
canActivate: [AuthGuardService],
canActivate: [AuthGuard],
component: ProvidersComponent,
},
{
@@ -47,7 +45,7 @@ const routes: Routes = [
},
{
path: "",
canActivate: [AuthGuardService],
canActivate: [AuthGuard],
children: [
{
path: "setup",
@@ -56,7 +54,7 @@ const routes: Routes = [
{
path: ":providerId",
component: ProvidersLayoutComponent,
canActivate: [ProviderGuardService],
canActivate: [ProviderGuard],
children: [
{ path: "", pathMatch: "full", redirectTo: "clients" },
{ path: "clients/create", component: CreateOrganizationComponent },
@@ -73,7 +71,7 @@ const routes: Routes = [
{
path: "people",
component: PeopleComponent,
canActivate: [ProviderTypeGuardService],
canActivate: [PermissionsGuard],
data: {
titleId: "people",
permissions: [Permissions.ManageUsers],
@@ -82,7 +80,7 @@ const routes: Routes = [
{
path: "events",
component: EventsComponent,
canActivate: [ProviderTypeGuardService],
canActivate: [PermissionsGuard],
data: {
titleId: "eventLogs",
permissions: [Permissions.AccessEventLogs],
@@ -102,7 +100,7 @@ const routes: Routes = [
{
path: "account",
component: AccountComponent,
canActivate: [ProviderTypeGuardService],
canActivate: [PermissionsGuard],
data: {
titleId: "myProvider",
permissions: [Permissions.ManageProvider],

View File

@@ -1,21 +1,17 @@
import { CommonModule } from "@angular/common";
import { ComponentFactoryResolver } from "@angular/core";
import { NgModule } from "@angular/core";
import { ComponentFactoryResolver, NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { JslibModule } from "jslib-angular/jslib.module";
import { ModalService } from "jslib-angular/services/modal.service";
import { ProviderGuardService } from "./services/provider-guard.service";
import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
import { WebProviderService } from "./services/webProvider.service";
import { ProvidersLayoutComponent } from "./providers-layout.component";
import { ProvidersRoutingModule } from "./providers-routing.module";
import { OssModule } from "src/app/oss.module";
import { AddOrganizationComponent } from "./clients/add-organization.component";
import { ClientsComponent } from "./clients/clients.component";
import { CreateOrganizationComponent } from "./clients/create-organization.component";
import { PermissionsGuard } from "./guards/provider-type.guard";
import { ProviderGuard } from "./guards/provider.guard";
import { AcceptProviderComponent } from "./manage/accept-provider.component";
import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component";
import { BulkRemoveComponent } from "./manage/bulk/bulk-remove.component";
@@ -23,17 +19,16 @@ import { EventsComponent } from "./manage/events.component";
import { ManageComponent } from "./manage/manage.component";
import { PeopleComponent } from "./manage/people.component";
import { UserAddEditComponent } from "./manage/user-add-edit.component";
import { ProvidersLayoutComponent } from "./providers-layout.component";
import { ProvidersRoutingModule } from "./providers-routing.module";
import { WebProviderService } from "./services/webProvider.service";
import { AccountComponent } from "./settings/account.component";
import { SettingsComponent } from "./settings/settings.component";
import { SetupProviderComponent } from "./setup/setup-provider.component";
import { SetupComponent } from "./setup/setup.component";
import { OssModule } from "src/app/oss.module";
@NgModule({
imports: [CommonModule, FormsModule, OssModule, ProvidersRoutingModule],
imports: [CommonModule, FormsModule, OssModule, JslibModule, ProvidersRoutingModule],
declarations: [
AcceptProviderComponent,
AccountComponent,
@@ -51,7 +46,7 @@ import { OssModule } from "src/app/oss.module";
SetupProviderComponent,
UserAddEditComponent,
],
providers: [WebProviderService, ProviderGuardService, ProviderTypeGuardService],
providers: [WebProviderService, ProviderGuard, PermissionsGuard],
})
export class ProvidersModule {
constructor(modalService: ModalService, componentFactoryResolver: ComponentFactoryResolver) {

View File

@@ -3,7 +3,6 @@ import { Injectable } from "@angular/core";
import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
import { ProviderAddOrganizationRequest } from "jslib-common/models/request/provider/providerAddOrganizationRequest";
@Injectable()

View File

@@ -6,9 +6,7 @@ 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 { SyncService } from "jslib-common/abstractions/sync.service";
import { ProviderUpdateRequest } from "jslib-common/models/request/provider/providerUpdateRequest";
import { ProviderResponse } from "jslib-common/models/response/provider/providerResponse";
@Component({

View File

@@ -1,7 +1,6 @@
import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.service";
@Component({
@@ -9,15 +8,11 @@ import { ProviderService } from "jslib-common/abstractions/provider.service";
templateUrl: "settings.component.html",
})
export class SettingsComponent {
constructor(
private route: ActivatedRoute,
private providerService: ProviderService,
private platformUtilsService: PlatformUtilsService
) {}
constructor(private route: ActivatedRoute, private providerService: ProviderService) {}
ngOnInit() {
this.route.parent.params.subscribe(async (params) => {
const provider = await this.providerService.get(params.providerId);
await this.providerService.get(params.providerId);
});
}
}

View File

@@ -20,7 +20,11 @@
<p>{{ "setupProviderLoginDesc" | i18n }}</p>
<hr />
<div class="d-flex">
<a routerLink="/" [queryParams]="{ email: email }" class="btn btn-primary btn-block">
<a
routerLink="/login"
[queryParams]="{ email: email }"
class="btn btn-primary btn-block"
>
{{ "logIn" | i18n }}
</a>
</div>

View File

@@ -16,6 +16,7 @@ export class SetupProviderComponent extends BaseAcceptComponent {
this.router.navigate(["/providers/setup"], { queryParams: qParams });
}
// tslint:disable-next-line
async unauthedHandler(qParams: any) {}
async unauthedHandler(qParams: any) {
// Empty
}
}

View File

@@ -1,12 +1,10 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
import { ValidationService } from "jslib-angular/services/validation.service";
import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { ValidationService } from "jslib-angular/services/validation.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { SyncService } from "jslib-common/abstractions/sync.service";

View File

@@ -7,6 +7,7 @@
"buttonAction": "https://www.sandbox.paypal.com/cgi-bin/webscr"
},
"dev": {
"port": 8080,
"allowedHosts": "auto"
}
}

View File

@@ -1 +1,9 @@
{}
{
"dev": {
"proxyApi": "http://localhost:4001",
"proxyIdentity": "http://localhost:33657",
"proxyEvents": "http://localhost:46274",
"proxyNotifications": "http://localhost:61841",
"port": 8081
}
}

2
jslib

Submodule jslib updated: e372bf242b...1cc4bed671

10151
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@bitwarden/web-vault",
"version": "2.25.1",
"version": "2022.05.0",
"license": "GPL-3.0",
"repository": "https://github.com/bitwarden/web",
"scripts": {
@@ -29,8 +29,8 @@
"dist:bit:selfhost": "npm run build:bit:selfhost:prod",
"deploy": "npm run dist:bit && gh-pages -d build",
"deploy:dev": "npm run dist:bit && gh-pages -d build -r git@github.com:kspearrin/bitwarden-web-dev.git",
"lint": "tslint 'src/**/*.ts' 'bitwarden_license/src/**/*.ts' && prettier --check .",
"lint:fix": "tslint 'src/**/*.ts' 'bitwarden_license/src/**/*.ts' --fix",
"lint": "eslint . && prettier --check .",
"lint:fix": "eslint . --fix",
"prettier": "prettier --write .",
"prepare": "husky install"
},
@@ -41,11 +41,18 @@
"@types/node": "^16.11.12",
"@types/webcrypto": "^0.0.28",
"@types/webpack": "^5.28.0",
"@typescript-eslint/eslint-plugin": "^5.10.1",
"@typescript-eslint/parser": "^5.10.1",
"autoprefixer": "^10.4.2",
"buffer": "^6.0.3",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^10.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.5.1",
"eslint": "^8.7.0",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-typescript": "^2.5.0",
"eslint-plugin-import": "^2.25.4",
"gh-pages": "^3.1.0",
"html-loader": "^3.0.1",
"html-webpack-injector": "1.1.4",
@@ -53,15 +60,17 @@
"husky": "^7.0.4",
"lint-staged": "^12.1.2",
"mini-css-extract-plugin": "^2.4.5",
"postcss": "^8.4.6",
"postcss-loader": "^6.2.1",
"prettier": "2.5.1",
"process": "^0.11.10",
"rimraf": "^3.0.2",
"sass": "^1.32.10",
"sass-loader": "^12.4.0",
"style-loader": "^3.3.1",
"tailwindcss": "^3.0.18",
"terser-webpack-plugin": "^5.2.5",
"ts-loader": "^9.2.5",
"tslint": "^6.1.3",
"tslint-loader": "^3.5.4",
"typescript": "4.3.5",
"util": "^0.12.4",
"webpack": "^5.64.4",
@@ -81,27 +90,30 @@
"@bitwarden/jslib-angular": "file:jslib/angular",
"@bitwarden/jslib-common": "file:jslib/common",
"bootstrap": "4.6.0",
"braintree-web-drop-in": "1.30.1",
"braintree-web-drop-in": "1.33.1",
"browser-hrtime": "^1.1.8",
"core-js": "^3.11.0",
"date-input-polyfill": "^2.14.0",
"font-awesome": "4.7.0",
"jquery": "3.6.0",
"jszip": "^3.7.1",
"ngx-infinite-scroll": "^10.0.1",
"ngx-toastr": "14.1.4",
"node-forge": "^1.3.1",
"popper.js": "1.16.1",
"qrious": "4.0.2",
"rxjs": "^7.4.0",
"sweetalert2": "^10.16.6",
"webcrypto-shim": "0.1.7",
"whatwg-fetch": "3.6.2"
"whatwg-fetch": "3.6.2",
"zone.js": "0.11.4"
},
"engines": {
"node": "~16",
"npm": "~8"
},
"lint-staged": {
"*": "prettier --ignore-unknown --write",
"./!(jslib)**": "prettier --ignore-unknown --write",
"*.ts": "eslint --fix",
"*.png": "node scripts/optimize.js"
}
}

4
postcss.config.js Normal file
View File

@@ -0,0 +1,4 @@
/* eslint-disable no-undef */
module.exports = {
plugins: [require("tailwindcss"), require("autoprefixer"), require("postcss-nested")],
};

View File

@@ -0,0 +1,9 @@
import { StateService as BaseStateService } from "jslib-common/abstractions/state.service";
import { StorageOptions } from "jslib-common/models/domain/storageOptions";
import { Account } from "src/models/account";
export abstract class StateService extends BaseStateService<Account> {
getRememberEmail: (options?: StorageOptions) => Promise<boolean>;
setRememberEmail: (value: boolean, options?: StorageOptions) => Promise<void>;
}

View File

@@ -23,7 +23,11 @@
<p>{{ "acceptEmergencyAccess" | i18n }}</p>
<hr />
<div class="d-flex">
<a routerLink="/" [queryParams]="{ email: email }" class="btn btn-primary btn-block">
<a
routerLink="/login"
[queryParams]="{ email: email }"
class="btn btn-primary btn-block"
>
{{ "logIn" | i18n }}
</a>
<a

View File

@@ -6,6 +6,7 @@ import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { EmergencyAccessAcceptRequest } from "jslib-common/models/request/emergencyAccessAcceptRequest";
import { BaseAcceptComponent } from "../common/base.accept.component";
@Component({

View File

@@ -24,7 +24,11 @@
<p>{{ "joinOrganizationDesc" | i18n }}</p>
<hr />
<div class="d-flex">
<a routerLink="/" [queryParams]="{ email: email }" class="btn btn-primary btn-block">
<a
routerLink="/login"
[queryParams]="{ email: email }"
class="btn btn-primary btn-block"
>
{{ "logIn" | i18n }}
</a>
<a

View File

@@ -8,12 +8,11 @@ import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { Utils } from "jslib-common/misc/utils";
import { Policy } from "jslib-common/models/domain/policy";
import { OrganizationUserAcceptRequest } from "jslib-common/models/request/organizationUserAcceptRequest";
import { OrganizationUserResetPasswordEnrollmentRequest } from "jslib-common/models/request/organizationUserResetPasswordEnrollmentRequest";
import { Utils } from "jslib-common/misc/utils";
import { Policy } from "jslib-common/models/domain/policy";
import { BaseAcceptComponent } from "../common/base.accept.component";
@Component({

View File

@@ -33,7 +33,7 @@
aria-hidden="true"
></i>
</button>
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
{{ "cancel" | i18n }}
</a>
</div>

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

@@ -1,6 +1,7 @@
import { Component, NgZone } from "@angular/core";
import { Router } from "@angular/router";
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
@@ -14,8 +15,6 @@ import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.serv
import { RouterService } from "../services/router.service";
import { LockComponent as BaseLockComponent } from "jslib-angular/components/lock.component";
@Component({
selector: "app-lock",
templateUrl: "lock.component.html",
@@ -56,10 +55,10 @@ export class LockComponent extends BaseLockComponent {
await super.ngOnInit();
this.onSuccessfulSubmit = async () => {
const previousUrl = this.routerService.getPreviousUrl();
if (previousUrl !== "/" && previousUrl.indexOf("lock") === -1) {
if (previousUrl && previousUrl !== "/" && previousUrl.indexOf("lock") === -1) {
this.successRoute = previousUrl;
}
this.router.navigate([this.successRoute]);
this.router.navigateByUrl(this.successRoute);
};
}
}

View File

@@ -86,7 +86,7 @@
[queryParams]="{ email: email }"
class="btn btn-outline-secondary btn-block ml-2 mt-0"
>
<i class="bwi bwi-pencil-square-o" aria-hidden="true"></i>
<i class="bwi bwi-pencil-square" aria-hidden="true"></i>
{{ "createAccount" | i18n }}
</a>
</div>

View File

@@ -1,22 +1,26 @@
import { Component, NgZone } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.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";
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { LoginComponent as BaseLoginComponent } from "jslib-angular/components/login.component";
import { PolicyData } from "jslib-common/models/data/policyData";
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
import { Policy } from "jslib-common/models/domain/policy";
import { ListResponse } from "jslib-common/models/response/listResponse";
import { PolicyResponse } from "jslib-common/models/response/policyResponse";
import { StateService } from "../../abstractions/state.service";
import { RouterService } from "../services/router.service";
@Component({
selector: "app-login",
@@ -24,13 +28,14 @@ import { Policy } from "jslib-common/models/domain/policy";
})
export class LoginComponent extends BaseLoginComponent {
showResetPasswordAutoEnrollWarning = false;
enforcedPasswordPolicyOptions: MasterPasswordPolicyOptions;
policies: ListResponse<PolicyResponse>;
constructor(
authService: AuthService,
router: Router,
i18nService: I18nService,
private route: ActivatedRoute,
stateService: StateService,
platformUtilsService: PlatformUtilsService,
environmentService: EnvironmentService,
passwordGenerationService: PasswordGenerationService,
@@ -38,7 +43,10 @@ export class LoginComponent extends BaseLoginComponent {
private apiService: ApiService,
private policyService: PolicyService,
logService: LogService,
ngZone: NgZone
ngZone: NgZone,
protected stateService: StateService,
private messagingService: MessagingService,
private routerService: RouterService
) {
super(
authService,
@@ -52,6 +60,9 @@ export class LoginComponent extends BaseLoginComponent {
logService,
ngZone
);
this.onSuccessfulLogin = async () => {
this.messagingService.send("setFullWidth");
};
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
}
@@ -61,58 +72,108 @@ export class LoginComponent extends BaseLoginComponent {
this.email = qParams.email;
}
if (qParams.premium != null) {
this.stateService.setLoginRedirect({ route: "/settings/premium" });
this.routerService.setPreviousUrl("/settings/premium");
} else if (qParams.org != null) {
this.stateService.setLoginRedirect({
route: "/settings/create-organization",
qParams: { plan: qParams.org },
const route = this.router.createUrlTree(["create-organization"], {
queryParams: { plan: qParams.org },
});
this.routerService.setPreviousUrl(route.toString());
}
// Are they coming from an email for sponsoring a families organization
if (qParams.sponsorshipToken != null) {
// After logging in redirect them to setup the families sponsorship
this.stateService.setLoginRedirect({
route: "/setup/families-for-enterprise",
qParams: { token: qParams.sponsorshipToken },
const route = this.router.createUrlTree(["setup/families-for-enterprise"], {
queryParams: { token: qParams.sponsorshipToken },
});
this.routerService.setPreviousUrl(route.toString());
}
await super.ngOnInit();
this.rememberEmail = await this.stateService.getRememberEmail();
});
const invite = await this.stateService.getOrganizationInvitation();
if (invite != null) {
let policyList: Policy[] = null;
try {
const policies = await this.apiService.getPoliciesByToken(
this.policies = await this.apiService.getPoliciesByToken(
invite.organizationId,
invite.token,
invite.email,
invite.organizationUserId
);
policyList = this.policyService.mapPoliciesFromToken(policies);
policyList = this.policyService.mapPoliciesFromToken(this.policies);
} catch (e) {
this.logService.error(e);
}
if (policyList != null) {
const result = this.policyService.getResetPasswordPolicyOptions(
const resetPasswordPolicy = this.policyService.getResetPasswordPolicyOptions(
policyList,
invite.organizationId
);
// Set to true if policy enabled and auto-enroll enabled
this.showResetPasswordAutoEnrollWarning = result[1] && result[0].autoEnrollEnabled;
this.showResetPasswordAutoEnrollWarning =
resetPasswordPolicy[1] && resetPasswordPolicy[0].autoEnrollEnabled;
this.enforcedPasswordPolicyOptions =
await this.policyService.getMasterPasswordPolicyOptions(policyList);
}
}
}
async goAfterLogIn() {
const loginRedirect = await this.stateService.getLoginRedirect();
if (loginRedirect != null) {
this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams });
await this.stateService.setLoginRedirect(null);
// Check master password against policy
if (this.enforcedPasswordPolicyOptions != null) {
const strengthResult = this.passwordGenerationService.passwordStrength(
this.masterPassword,
this.getPasswordStrengthUserInput()
);
const masterPasswordScore = strengthResult == null ? null : strengthResult.score;
// If invalid, save policies and require update
if (
!this.policyService.evaluateMasterPassword(
masterPasswordScore,
this.masterPassword,
this.enforcedPasswordPolicyOptions
)
) {
const policiesData: { [id: string]: PolicyData } = {};
this.policies.data.map((p) => (policiesData[p.id] = new PolicyData(p)));
await this.policyService.replace(policiesData);
this.router.navigate(["update-password"]);
return;
}
}
const previousUrl = this.routerService.getPreviousUrl();
if (previousUrl) {
this.router.navigateByUrl(previousUrl);
} else {
this.router.navigate([this.successRoute]);
}
}
async submit() {
await this.stateService.setRememberEmail(this.rememberEmail);
if (!this.rememberEmail) {
await this.stateService.setRememberedEmail(null);
}
await super.submit();
}
private getPasswordStrengthUserInput() {
let userInput: string[] = [];
const atPosition = this.email.indexOf("@");
if (atPosition > -1) {
userInput = userInput.concat(
this.email
.substr(0, atPosition)
.trim()
.toLowerCase()
.split(/[^A-Za-z0-9]/)
);
}
return userInput;
}
}

View File

@@ -33,7 +33,7 @@
aria-hidden="true"
></i>
</button>
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
{{ "cancel" | i18n }}
</a>
</div>

View File

@@ -5,7 +5,6 @@ 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 { DeleteRecoverRequest } from "jslib-common/models/request/deleteRecoverRequest";
@Component({

View File

@@ -7,7 +7,7 @@
<p>
{{ "recoverAccountTwoStepDesc" | i18n }}
<a
href="https://help.bitwarden.com/article/lost-two-step-device/"
href="https://bitwarden.com/help/lost-two-step-device/"
target="_blank"
rel="noopener"
>{{ "learnMore" | i18n }}</a
@@ -65,7 +65,7 @@
aria-hidden="true"
></i>
</button>
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
{{ "cancel" | i18n }}
</a>
</div>

View File

@@ -7,7 +7,6 @@ import { CryptoService } from "jslib-common/abstractions/crypto.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 { TwoFactorRecoveryRequest } from "jslib-common/models/request/twoFactorRecoveryRequest";
@Component({

View File

@@ -1,44 +1,100 @@
<div class="layout" [ngClass]="['layout', layout]">
<header class="header" *ngIf="layout === 'enterprise2'">
<!-- TEAMS 1 Header -->
<header
class="header"
*ngIf="
layout === 'default' ||
layout === 'teams' ||
layout === 'teams1' ||
layout === 'teams2' ||
layout === 'enterprise' ||
layout === 'enterprise1' ||
layout === 'enterprise2' ||
layout === 'cnetcmpgnent' ||
layout === 'cnetcmpgnteams' ||
layout === 'cnetcmpgnind'
"
>
<div class="container">
<div class="row">
<div class="col-7">
<img
alt="Bitwarden"
class="logo mb-2"
src="../../images/register-layout/logo-horizontal-white.png"
src="../../images/register-layout/logo-horizontal-white.svg"
/>
</div>
</div>
</div>
</header>
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" class="container" ngNativeValidate>
<div class="row">
<div class="col-7" *ngIf="layout">
<div class="mt-5">
<div *ngIf="layout === 'enterprise2'">
<h2>Companies globally trust Bitwarden for password management.</h2>
<p>Start your 7-day free trial!</p>
<p class="highlight">Quickly deploy your <b>organization</b></p>
<p>Use Bitwarden across all platforms</p>
<!-- Default Body -->
<div
*ngIf="
layout === 'teams' ||
layout === 'enterprise' ||
layout === 'enterprise1' ||
layout === 'default'
"
>
<h1>The Bitwarden Password Manager</h1>
<h2>
Trusted by millions of individuals, teams, and organizations worldwide for secure
password storage and sharing.
</h2>
<p>Store logins, secure notes, and more</p>
<p>Collaborate and share securely</p>
<figure>
<figcaption>
<cite>
<img src="../../images/register-layout/wired-logo.png" alt="Wired" />
</cite>
</figcaption>
<blockquote>
"Bitwarden has become a popular choice among open-source software advocates. After
using it for a few months, I can see why." - February 2020
</blockquote>
</figure>
<p>Access anywhere on any device</p>
<p>Create your account to get started</p>
</div>
<div *ngIf="layout === 'enterprise3'">
<p>Enterprise 3 layout</p>
<!-- Teams & Enterprise Body -->
<div *ngIf="layout === 'teams1' || layout === 'teams2' || layout === 'enterprise2'">
<h1>
Start Your <span *ngIf="layout === 'teams1' || layout === 'teams1'">Teams<br /></span
><span *ngIf="layout === 'enterprise2'">Enterprise</span> Free Trial Now
</h1>
<h2>
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
password storage and sharing.
</h2>
<p>Collaborate and share securely</p>
<p>Deploy and manage quickly and easily</p>
<p>Access anywhere on any device</p>
<p>Create your account to get started</p>
</div>
<div *ngIf="layout === 'enterprise4'">
<p>Enterprise 4 layout</p>
<!-- CNET Campaign Teams & Enterprise Body -->
<div *ngIf="layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnent'">
<h1>
Start Your <span *ngIf="layout === 'cnetcmpgnteams'">Teams<br /></span
><span *ngIf="layout === 'cnetcmpgnent'">Enterprise</span> Free Trial Now
</h1>
<h2>
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
password storage and sharing.
</h2>
<p>Collaborate and share securely</p>
<p>Deploy and manage quickly and easily</p>
<p>Access anywhere on any device</p>
<p>Create your account to get started</p>
</div>
<!-- CNET Campaign Premium Body -->
<div *ngIf="layout === 'cnetcmpgnind'">
<h1>Start Your Premium Account Now</h1>
<h2>
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
password storage and sharing.
</h2>
<p>Store logins, secure notes, and more</p>
<p>Secure your account with advanced two-step login</p>
<p>Access anywhere on any device</p>
<p>Create your account to get started</p>
</div>
</div>
</div>
@@ -202,7 +258,7 @@
aria-hidden="true"
></i>
</button>
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
{{ "cancel" | i18n }}
</a>
</div>
@@ -212,5 +268,88 @@
</div>
</div>
</div>
<div class="row">
<div class="col-7 d-flex align-items-center">
<div
*ngIf="
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
"
>
<figure>
<figcaption>
<cite>
<img
src="../../images/register-layout/cnet-logo.svg"
class="w-25 d-block mx-auto"
alt="cnet logo"
/>
</cite>
</figcaption>
<blockquote class="mx-auto text-center px-4">
"No more excuses; start using Bitwarden today. The identity you save could be your
own. The money definitely will be."
</blockquote>
</figure>
</div>
<div
*ngIf="
layout === 'teams' ||
layout === 'teams1' ||
layout === 'teams2' ||
layout === 'enterprise' ||
layout === 'enterprise1' ||
layout === 'enterprise2' ||
layout === 'default'
"
>
<figure>
<figcaption>
<cite>
<img
src="../../images/register-layout/forbes-logo.svg"
class="w-25 d-block mx-auto"
alt="Forbes Logo"
/>
</cite>
</figcaption>
<blockquote class="mx-auto text-center px-4">
“Bitwarden boasts the backing of some of the world's best security experts and an
attractive, easy-to-use interface”
</blockquote>
</figure>
</div>
</div>
<div
*ngIf="
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
"
class="col-5 d-flex align-items-center justify-content-center"
>
<img
src="../../images/register-layout/usnews-360-badge.svg"
class="w-50 d-block"
alt="US News 360 Reviews Best Password Manager"
/>
</div>
<div
*ngIf="
layout === 'teams' ||
layout === 'teams1' ||
layout === 'teams2' ||
layout === 'enterprise' ||
layout === 'enterprise1' ||
layout === 'enterprise2' ||
layout === 'default'
"
class="col-5 d-flex align-items-center justify-content-center"
>
<img
src="../../images/register-layout/usnews-360-badge.svg"
class="w-50 d-block"
alt="US News 360 Reviews Best Password Manager"
/>
</div>
</div>
</form>
</div>

View File

@@ -1,8 +1,8 @@
import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
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 { CryptoService } from "jslib-common/abstractions/crypto.service";
@@ -13,15 +13,13 @@ import { PasswordGenerationService } from "jslib-common/abstractions/passwordGen
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { RegisterComponent as BaseRegisterComponent } from "jslib-angular/components/register.component";
import { PolicyData } from "jslib-common/models/data/policyData";
import { MasterPasswordPolicyOptions } from "jslib-common/models/domain/masterPasswordPolicyOptions";
import { Policy } from "jslib-common/models/domain/policy";
import { PolicyData } from "jslib-common/models/data/policyData";
import { ReferenceEventRequest } from "jslib-common/models/request/referenceEventRequest";
import { RouterService } from "../services/router.service";
@Component({
selector: "app-register",
templateUrl: "register.component.html",
@@ -45,7 +43,8 @@ export class RegisterComponent extends BaseRegisterComponent {
passwordGenerationService: PasswordGenerationService,
private policyService: PolicyService,
environmentService: EnvironmentService,
logService: LogService
logService: LogService,
private routerService: RouterService
) {
super(
authService,
@@ -68,14 +67,14 @@ export class RegisterComponent extends BaseRegisterComponent {
this.email = qParams.email;
}
if (qParams.premium != null) {
this.stateService.setLoginRedirect({ route: "/settings/premium" });
this.routerService.setPreviousUrl("/settings/premium");
} else if (qParams.org != null) {
this.showCreateOrgMessage = true;
this.referenceData.flow = qParams.org;
this.stateService.setLoginRedirect({
route: "/settings/create-organization",
qParams: { plan: qParams.org },
const route = this.router.createUrlTree(["create-organization"], {
queryParams: { plan: qParams.org },
});
this.routerService.setPreviousUrl(route.toString());
}
if (qParams.layout != null) {
this.layout = this.referenceData.layout = qParams.layout;
@@ -92,10 +91,10 @@ export class RegisterComponent extends BaseRegisterComponent {
// Are they coming from an email for sponsoring a families organization
if (qParams.sponsorshipToken != null) {
// After logging in redirect them to setup the families sponsorship
this.stateService.setLoginRedirect({
route: "/setup/families-for-enterprise",
qParams: { token: qParams.sponsorshipToken },
const route = this.router.createUrlTree(["setup/families-for-enterprise"], {
queryParams: { plan: qParams.sponsorshipToken },
});
this.routerService.setPreviousUrl(route.toString());
}
if (this.referenceData.id === "") {
this.referenceData.id = null;

View File

@@ -1,6 +1,7 @@
import { Component } 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 { 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 { SetPasswordComponent as BaseSetPasswordComponent } from "jslib-angular/components/set-password.component";
@Component({
selector: "app-set-password",
templateUrl: "set-password.component.html",

View File

@@ -41,7 +41,7 @@
aria-hidden="true"
></i>
</button>
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
{{ "cancel" | i18n }}
</a>
</div>

View File

@@ -1,8 +1,8 @@
import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
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 { PasswordGenerationService } from "jslib-common/abstractions/passwordGen
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { SsoComponent as BaseSsoComponent } from "jslib-angular/components/sso.component";
@Component({
selector: "app-sso",
templateUrl: "sso.component.html",

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

@@ -114,6 +114,9 @@
<p>{{ "noTwoStepProviders2" | i18n }}</p>
</ng-container>
<hr />
<div [hidden]="!showCaptcha()">
<iframe id="hcaptcha_iframe" height="80"></iframe>
</div>
<div class="d-flex mb-3">
<button
type="submit"
@@ -135,7 +138,7 @@
aria-hidden="true"
></i>
</button>
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
{{ "cancel" | i18n }}
</a>
</div>

View File

@@ -1,20 +1,20 @@
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
import { ModalService } from "jslib-angular/services/modal.service";
import { ApiService } from "jslib-common/abstractions/api.service";
import { AppIdService } from "jslib-common/abstractions/appId.service";
import { AuthService } from "jslib-common/abstractions/auth.service";
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
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 { ModalService } from "jslib-angular/services/modal.service";
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
import { TwoFactorComponent as BaseTwoFactorComponent } from "jslib-angular/components/two-factor.component";
import { RouterService } from "../services/router.service";
import { TwoFactorOptionsComponent } from "./two-factor-options.component";
@@ -36,7 +36,10 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
environmentService: EnvironmentService,
private modalService: ModalService,
route: ActivatedRoute,
logService: LogService
logService: LogService,
twoFactorService: TwoFactorService,
appIdService: AppIdService,
private routerService: RouterService
) {
super(
authService,
@@ -48,7 +51,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
environmentService,
stateService,
route,
logService
logService,
twoFactorService,
appIdService
);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
}
@@ -71,10 +76,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
}
async goAfterLogIn() {
const loginRedirect = await this.stateService.getLoginRedirect();
if (loginRedirect != null) {
this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams });
await this.stateService.setLoginRedirect(null);
const previousUrl = this.routerService.getPreviousUrl();
if (previousUrl) {
this.router.navigateByUrl(previousUrl);
} else {
this.router.navigate([this.successRoute], {
queryParams: {

View File

@@ -0,0 +1,90 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate autocomplete="off">
<div class="row justify-content-md-center mt-5">
<div class="col-4">
<p class="lead text-center mb-4">{{ "updateMasterPassword" | i18n }}</p>
<div class="card d-block">
<div class="card-body">
<app-callout type="warning">{{ "masterPasswordInvalidWarning" | i18n }} </app-callout>
<app-callout
type="info"
[enforcedPolicyOptions]="enforcedPolicyOptions"
*ngIf="enforcedPolicyOptions"
></app-callout>
<form
#form
(ngSubmit)="submit()"
[appApiAction]="formPromise"
ngNativeValidate
autocomplete="off"
>
<div class="row">
<div class="col-6">
<div class="form-group">
<label for="currentMasterPassword">{{ "currentMasterPass" | i18n }}</label>
<input
id="currentMasterPassword"
type="password"
name="MasterPasswordHash"
class="form-control"
[(ngModel)]="currentMasterPassword"
required
appInputVerbatim
/>
</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div class="form-group">
<label for="newMasterPassword">{{ "newMasterPass" | i18n }}</label>
<input
id="newMasterPassword"
type="password"
name="NewMasterPasswordHash"
class="form-control mb-1"
[(ngModel)]="masterPassword"
(input)="updatePasswordStrength()"
required
appInputVerbatim
autocomplete="new-password"
/>
<app-password-strength
[score]="masterPasswordScore"
[showText]="true"
></app-password-strength>
</div>
</div>
<div class="col-6">
<div class="form-group">
<label for="masterPasswordRetype">{{ "confirmNewMasterPass" | i18n }}</label>
<input
id="masterPasswordRetype"
type="password"
name="MasterPasswordRetype"
class="form-control"
[(ngModel)]="masterPasswordRetype"
required
appInputVerbatim
autocomplete="new-password"
/>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
<i
class="fa fa-spinner fa-spin"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span>{{ "changeMasterPassword" | i18n }}</span>
</button>
<button (click)="cancel()" type="button" class="btn btn-outline-secondary">
<span>{{ "cancel" | i18n }}</span>
</button>
</form>
</div>
</div>
</div>
</div>
</form>

View File

@@ -0,0 +1,48 @@
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "jslib-angular/components/update-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";
import { LogService } from "jslib-common/abstractions/log.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { UserVerificationService } from "jslib-common/abstractions/userVerification.service";
@Component({
selector: "app-update-password",
templateUrl: "update-password.component.html",
})
export class UpdatePasswordComponent extends BaseUpdatePasswordComponent {
constructor(
router: Router,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
passwordGenerationService: PasswordGenerationService,
policyService: PolicyService,
cryptoService: CryptoService,
messagingService: MessagingService,
apiService: ApiService,
logService: LogService,
stateService: StateService,
userVerificationService: UserVerificationService
) {
super(
router,
i18nService,
platformUtilsService,
passwordGenerationService,
policyService,
cryptoService,
messagingService,
apiService,
stateService,
userVerificationService,
logService
);
}
}

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";
@@ -8,10 +9,8 @@ import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from "jslib-angular/components/update-temp-password.component";
import { StateService } from "jslib-common/abstractions/state.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
@Component({
selector: "app-update-temp-password",

View File

@@ -1,6 +1,5 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
import { ApiService } from "jslib-common/abstractions/api.service";
@@ -8,7 +7,6 @@ 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 { VerifyEmailRequest } from "jslib-common/models/request/verifyEmailRequest";
@Component({

View File

@@ -23,7 +23,7 @@
aria-hidden="true"
></i>
</button>
<a routerLink="/" class="btn btn-outline-secondary btn-block ml-2 mt-0">
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
{{ "cancel" | i18n }}
</a>
</div>

View File

@@ -1,13 +1,11 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
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 { VerifyDeleteRecoverRequest } from "jslib-common/models/request/verifyDeleteRecoverRequest";
@Component({

View File

@@ -21,14 +21,10 @@ import { PolicyService } from "jslib-common/abstractions/policy.service";
import { SearchService } from "jslib-common/abstractions/search.service";
import { SettingsService } from "jslib-common/abstractions/settings.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { StorageService } from "jslib-common/abstractions/storage.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
import { TokenService } from "jslib-common/abstractions/token.service";
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
import { PolicyListService } from "./services/policy-list.service";
import { RouterService } from "./services/router.service";
import { DisableSendPolicy } from "./organizations/policies/disable-send.component";
import { MasterPasswordPolicy } from "./organizations/policies/master-password.component";
import { PasswordGeneratorPolicy } from "./organizations/policies/password-generator.component";
@@ -38,6 +34,8 @@ import { ResetPasswordPolicy } from "./organizations/policies/reset-password.com
import { SendOptionsPolicy } from "./organizations/policies/send-options.component";
import { SingleOrgPolicy } from "./organizations/policies/single-org.component";
import { TwoFactorAuthenticationPolicy } from "./organizations/policies/two-factor-authentication.component";
import { PolicyListService } from "./services/policy-list.service";
import { RouterService } from "./services/router.service";
const BroadcasterSubscriptionId = "AppComponent";
const IdleTimeout = 60000 * 10; // 10 minutes
@@ -93,11 +91,17 @@ export class AppComponent implements OnDestroy, OnInit {
this.ngZone.run(async () => {
switch (message.command) {
case "loggedIn":
this.notificationsService.updateConnection(false);
break;
case "loggedOut":
this.routerService.setPreviousUrl(null);
this.notificationsService.updateConnection(false);
break;
case "unlocked":
this.notificationsService.updateConnection(false);
break;
case "authBlocked":
this.routerService.setPreviousUrl(message.url);
this.router.navigate(["/"]);
break;
case "logout":
@@ -111,13 +115,13 @@ export class AppComponent implements OnDestroy, OnInit {
this.router.navigate(["lock"]);
break;
case "lockedUrl":
window.setTimeout(() => this.routerService.setPreviousUrl(message.url), 500);
this.routerService.setPreviousUrl(message.url);
break;
case "syncStarted":
break;
case "syncCompleted":
break;
case "upgradeOrganization":
case "upgradeOrganization": {
const upgradeConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("upgradeOrganizationDesc"),
this.i18nService.t("upgradeOrganization"),
@@ -133,7 +137,8 @@ export class AppComponent implements OnDestroy, OnInit {
]);
}
break;
case "premiumRequired":
}
case "premiumRequired": {
const premiumConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("premiumRequiredDesc"),
this.i18nService.t("premiumRequired"),
@@ -144,7 +149,8 @@ export class AppComponent implements OnDestroy, OnInit {
this.router.navigate(["settings/premium"]);
}
break;
case "emailVerificationRequired":
}
case "emailVerificationRequired": {
const emailVerificationConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("emailVerificationRequiredDesc"),
this.i18nService.t("emailVerificationRequired"),
@@ -153,10 +159,11 @@ export class AppComponent implements OnDestroy, OnInit {
);
if (emailVerificationConfirmed) {
this.platformUtilsService.launchUri(
"https://bitwarden.com/help/article/create-bitwarden-account/"
"https://bitwarden.com/help/create-bitwarden-account/"
);
}
break;
}
case "showToast":
this.showToast(message);
break;
@@ -210,7 +217,6 @@ export class AppComponent implements OnDestroy, OnInit {
await Promise.all([
this.eventService.clearEvents(),
this.syncService.setLastSync(new Date(0)),
this.tokenService.clearToken(),
this.cryptoService.clearKeys(),
this.settingsService.clear(userId),
this.cipherService.clear(userId),

View File

@@ -1,11 +1,8 @@
import { InfiniteScrollModule } from "ngx-infinite-scroll";
import { DragDropModule } from "@angular/cdk/drag-drop";
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { BitwardenToastModule } from "jslib-angular/components/toastr.component";
import { InfiniteScrollModule } from "ngx-infinite-scroll";
import { AppComponent } from "./app.component";
import { OssRoutingModule } from "./oss-routing.module";
@@ -19,11 +16,6 @@ import { WildcardRoutingModule } from "./wildcard-routing.module";
BrowserAnimationsModule,
FormsModule,
ServicesModule,
BitwardenToastModule.forRoot({
maxOpened: 5,
autoDismiss: true,
closeButton: true,
}),
InfiniteScrollModule,
DragDropModule,
OssRoutingModule,

View File

@@ -1,6 +1,5 @@
import { Directive, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
import { I18nService } from "jslib-common/abstractions/i18n.service";
@@ -31,7 +30,6 @@ export abstract class BaseAcceptComponent implements OnInit {
ngOnInit() {
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
await this.stateService.setLoginRedirect(null);
let error = this.requiredParameters.some((e) => qParams?.[e] == null || qParams[e] === "");
let errorMessage: string = null;
if (!error) {
@@ -45,11 +43,6 @@ export abstract class BaseAcceptComponent implements OnInit {
errorMessage = e.message;
}
} else {
await this.stateService.setLoginRedirect({
route: this.getRedirectRoute(),
qParams: qParams,
});
this.email = qParams.email;
await this.unauthedHandler(qParams);
}
@@ -67,10 +60,4 @@ export abstract class BaseAcceptComponent implements OnInit {
this.loading = false;
});
}
getRedirectRoute() {
const urlTree = this.router.parseUrl(this.router.url);
urlTree.queryParams = {};
return urlTree.toString();
}
}

View File

@@ -4,11 +4,9 @@ import { ExportService } from "jslib-common/abstractions/export.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 { EventView } from "jslib-common/models/view/eventView";
import { EventResponse } from "jslib-common/models/response/eventResponse";
import { ListResponse } from "jslib-common/models/response/listResponse";
import { EventView } from "jslib-common/models/view/eventView";
import { EventService } from "src/app/services/event.service";
@@ -19,7 +17,7 @@ export abstract class BaseEventsComponent {
events: EventView[];
start: string;
end: string;
dirtyDates: boolean = true;
dirtyDates = true;
continuationToken: string;
refreshPromise: Promise<any>;
exportPromise: Promise<any>;
@@ -124,17 +122,20 @@ export abstract class BaseEventsComponent {
const userId = r.actingUserId == null ? r.userId : r.actingUserId;
const eventInfo = await this.eventService.getEventInfo(r);
const user = this.getUserName(r, userId);
const userName = user != null ? user.name : this.i18nService.t("unknown");
return new EventView({
message: eventInfo.message,
humanReadableMessage: eventInfo.humanReadableMessage,
appIcon: eventInfo.appIcon,
appName: eventInfo.appName,
userId: userId,
userName: user != null ? user.name : this.i18nService.t("unknown"),
userName: r.installationId != null ? `Installation: ${r.installationId}` : userName,
userEmail: user != null ? user.email : "",
date: r.date,
ip: r.ipAddress,
type: r.type,
installationId: r.installationId,
});
})
);

View File

@@ -1,5 +1,9 @@
import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
import { ModalService } from "jslib-angular/services/modal.service";
import { ValidationService } from "jslib-angular/services/validation.service";
import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
@@ -7,24 +11,15 @@ import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { SearchService } from "jslib-common/abstractions/search.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { ModalService } from "jslib-angular/services/modal.service";
import { ValidationService } from "jslib-angular/services/validation.service";
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
import { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
import { OrganizationUserType } from "jslib-common/enums/organizationUserType";
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
import { ProviderUserType } from "jslib-common/enums/providerUserType";
import { Utils } from "jslib-common/misc/utils";
import { ListResponse } from "jslib-common/models/response/listResponse";
import { OrganizationUserUserDetailsResponse } from "jslib-common/models/response/organizationUserResponse";
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
import { Utils } from "jslib-common/misc/utils";
import { UserConfirmComponent } from "../organizations/manage/user-confirm.component";
type StatusType = OrganizationUserStatusType | ProviderUserStatusType;

View File

@@ -1,4 +1,5 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { Utils } from "jslib-common/misc/utils";
@Component({

View File

@@ -0,0 +1,68 @@
<div *ngIf="loaded && activeOrganization != null" class="tw-flex">
<button
class="tw-flex tw-items-center tw-bg-background-alt tw-border-none"
type="button"
id="pickerButton"
[appA11yTitle]="'organizationPicker' | i18n"
[bitMenuTriggerFor]="orgPickerMenu"
>
<app-avatar
[data]="activeOrganization.name"
size="45"
[circle]="true"
[dynamic]="true"
></app-avatar>
<div class="tw-flex">
<div class="org-name tw-ml-3">
<span>{{ activeOrganization.name }}</span>
<small class="tw-text-muted">{{ "organization" | i18n }}</small>
</div>
<div class="tw-ml-3">
<i class="bwi bwi-angle-down tw-text-main" aria-hidden="true"></i>
</div>
</div>
</button>
<div>
<div
class="tw-ml-3 tw-border tw-border-solid tw-rounded tw-border-danger-500 tw-text-danger"
*ngIf="!activeOrganization.enabled"
>
<div class="tw-py-2 tw-px-5">
<i class="bwi bwi-exclamation-triangle" aria-hidden="true"></i>
{{ "organizationIsDisabled" | i18n }}
</div>
</div>
<div
class="tw-ml-3 tw-border tw-border-solid tw-rounded tw-border-info-500 tw-text-info"
*ngIf="activeOrganization.isProviderUser"
>
<div class="tw-py-2 tw-px-5">
<i class="bwi bwi-exclamation-triangle" aria-hidden="true"></i>
{{ "accessingUsingProvider" | i18n: activeOrganization.providerName }}
</div>
</div>
</div>
<bit-menu #orgPickerMenu>
<ul aria-labelledby="pickerButton" class="tw-p-0 tw-m-0">
<li *ngFor="let org of organizations" class="tw-list-none tw-flex tw-flex-col" role="none">
<a bit-menu-item [routerLink]="['/organizations', org.id]">
<i
class="bwi bwi-check mr-2"
[ngClass]="org.id === activeOrganization.id ? 'visible' : 'invisible'"
>
<span class="tw-sr-only">{{ "currentOrganization" | i18n }}</span>
</i>
{{ org.name }}
</a>
</li>
<bit-menu-divider></bit-menu-divider>
<li class="tw-list-none" role="none">
<a bit-menu-item routerLink="/create-organization">
<i class="bwi bwi-plus mr-2"></i>
{{ "newOrganization" | i18n }}</a
>
</li>
</ul>
</bit-menu>
</div>

View File

@@ -0,0 +1,34 @@
import { Component, Input, OnInit } from "@angular/core";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { Utils } from "jslib-common/misc/utils";
import { Organization } from "jslib-common/models/domain/organization";
import { NavigationPermissionsService } from "../organizations/services/navigation-permissions.service";
@Component({
selector: "app-organization-switcher",
templateUrl: "organization-switcher.component.html",
})
export class OrganizationSwitcherComponent implements OnInit {
constructor(private organizationService: OrganizationService, private i18nService: I18nService) {}
@Input() activeOrganization: Organization = null;
organizations: Organization[] = [];
loaded = false;
async ngOnInit() {
await this.load();
}
async load() {
const orgs = await this.organizationService.getAll();
this.organizations = orgs
.filter((org) => NavigationPermissionsService.canAccessAdmin(org))
.sort(Utils.getSortFunction(this.i18nService, "name"));
this.loaded = true;
}
}

View File

@@ -0,0 +1,19 @@
import { Component } from "@angular/core";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
@Component({
selector: "app-premium-badge",
template: `
<button *appNotPremium bit-badge badgeType="success" (click)="premiumRequired()">
{{ "premium" | i18n }}
</button>
`,
})
export class PremiumBadgeComponent {
constructor(private messagingService: MessagingService) {}
premiumRequired() {
this.messagingService.send("premiumRequired");
}
}

View File

@@ -0,0 +1,22 @@
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router";
import { AuthService } from "jslib-common/abstractions/auth.service";
import { AuthenticationStatus } from "jslib-common/enums/authenticationStatus";
@Injectable()
export class HomeGuard implements CanActivate {
constructor(private router: Router, private authService: AuthService) {}
async canActivate(route: ActivatedRouteSnapshot) {
const authStatus = await this.authService.getAuthStatus();
if (authStatus === AuthenticationStatus.LoggedOut) {
return this.router.createUrlTree(["/login"], { queryParams: route.queryParams });
}
if (authStatus === AuthenticationStatus.Locked) {
return this.router.createUrlTree(["/lock"], { queryParams: route.queryParams });
}
return this.router.createUrlTree(["/vault"], { queryParams: route.queryParams });
}
}

View File

@@ -8,7 +8,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
})
export class FooterComponent implements OnInit {
version: string;
year: string = "2015";
year = "2015";
constructor(private platformUtilsService: PlatformUtilsService) {}

View File

@@ -8,7 +8,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
})
export class FrontendLayoutComponent implements OnInit, OnDestroy {
version: string;
year: string = "2015";
year = "2015";
constructor(private platformUtilsService: PlatformUtilsService) {}

View File

@@ -6,11 +6,22 @@
<div class="collapse navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/vault">{{ "myVault" | i18n }}</a>
<a class="nav-link" routerLink="/vault">{{ "vaults" | i18n }}</a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/sends">{{ "send" | i18n }}</a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/tools">{{ "tools" | i18n }}</a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/reports">{{ "reports" | i18n }}</a>
</li>
<li *ngIf="organizations.length >= 1" class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/organizations', organizations[0].id]">{{
"organizations" | i18n
}}</a>
</li>
<ng-container *ngIf="providers.length >= 1">
<li class="nav-item" routerLinkActive="active" *ngIf="providers.length == 1">
<a class="nav-link" [routerLink]="['/providers', providers[0].id]">{{
@@ -21,28 +32,24 @@
<a class="nav-link" routerLink="/providers">{{ "provider" | i18n }}</a>
</li>
</ng-container>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/tools">{{ "tools" | i18n }}</a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/settings">{{ "settings" | i18n }}</a>
</li>
</ul>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item dropdown">
<a
class="nav-item nav-link dropdown-toggle"
href="#"
id="nav-profile"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
<li>
<button
[bitMenuTriggerFor]="accountMenu"
class="tw-border-0 tw-bg-transparent tw-text-alt2 tw-opacity-70 hover:tw-opacity-90"
>
<i class="bwi bwi-user-circle bwi-lg" aria-hidden="true"></i>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="nav-profile">
<div class="dropdown-item-text d-flex align-items-center" *ngIf="name" appStopProp>
<i class="bwi bwi-caret-down bwi-sm" aria-hidden="true"></i>
</button>
<bit-menu class="dropdown-menu" #accountMenu>
<div class="tw-max-w-[300px] tw-min-w-[200px] tw-flex tw-flex-col">
<div
class="tw-flex tw-items-center tw-leading-tight tw-text-info tw-py-1 tw-px-4"
*ngIf="name"
appStopProp
>
<app-avatar
[data]="name"
[email]="email"
@@ -50,39 +57,37 @@
fontSize="14"
[circle]="true"
></app-avatar>
<div class="ml-2 overflow-hidden">
<div class="tw-ml-2 tw-block tw-overflow-hidden tw-whitespace-nowrap">
<span>{{ "loggedInAs" | i18n }}</span>
<small class="text-muted">{{ name }}</small>
<small class="tw-text-muted tw-block tw-overflow-hidden tw-whitespace-nowrap">{{
name
}}</small>
</div>
</div>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" routerLink="/settings/account">
<bit-menu-divider></bit-menu-divider>
<a bit-menu-item routerLink="/settings/account">
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
{{ "myAccount" | i18n }}
{{ "accountSettings" | i18n }}
</a>
<a class="dropdown-item" href="https://help.bitwarden.com" target="_blank" rel="noopener">
<a bit-menu-item href="https://bitwarden.com/help/" target="_blank" rel="noopener">
<i class="bwi bwi-fw bwi-question-circle" aria-hidden="true"></i>
{{ "getHelp" | i18n }}
</a>
<a
class="dropdown-item"
href="https://bitwarden.com/download/"
target="_blank"
rel="noopener"
>
<a bit-menu-item href="https://bitwarden.com/download/" target="_blank" rel="noopener">
<i class="bwi bwi-fw bwi-download" aria-hidden="true"></i>
{{ "getApps" | i18n }}
</a>
<div class="dropdown-divider"></div>
<button type="button" class="dropdown-item" (click)="lock()">
<bit-menu-divider></bit-menu-divider>
<button bit-menu-item type="button" (click)="lock()">
<i class="bwi bwi-fw bwi-lock" aria-hidden="true"></i>
{{ "lockNow" | i18n }}
</button>
<button type="button" class="dropdown-item" (click)="logOut()">
<button bit-menu-item type="button" (click)="logOut()">
<i class="bwi bwi-fw bwi-sign-out" aria-hidden="true"></i>
{{ "logOut" | i18n }}
</button>
</div>
</bit-menu>
</li>
</ul>
</div>

View File

@@ -1,13 +1,19 @@
import { Component, OnInit } from "@angular/core";
import { Component, NgZone, OnInit } from "@angular/core";
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
import { TokenService } from "jslib-common/abstractions/token.service";
import { Utils } from "jslib-common/misc/utils";
import { Organization } from "jslib-common/models/domain/organization";
import { Provider } from "jslib-common/models/domain/provider";
import { NavigationPermissionsService as OrgNavigationPermissionsService } from "../organizations/services/navigation-permissions.service";
@Component({
selector: "app-navbar",
templateUrl: "navbar.component.html",
@@ -17,13 +23,18 @@ export class NavbarComponent implements OnInit {
name: string;
email: string;
providers: Provider[] = [];
organizations: Organization[] = [];
constructor(
private messagingService: MessagingService,
private platformUtilsService: PlatformUtilsService,
private tokenService: TokenService,
private providerService: ProviderService,
private syncService: SyncService
private syncService: SyncService,
private organizationService: OrganizationService,
private i18nService: I18nService,
private broadcasterService: BroadcasterService,
private ngZone: NgZone
) {
this.selfHosted = this.platformUtilsService.isSelfHost();
}
@@ -35,11 +46,32 @@ export class NavbarComponent implements OnInit {
this.name = this.email;
}
// Ensure provides are loaded
// Ensure providers and organizations are loaded
if ((await this.syncService.getLastSync()) == null) {
await this.syncService.fullSync(false);
}
this.providers = await this.providerService.getAll();
this.organizations = await this.buildOrganizations();
this.broadcasterService.subscribe(this.constructor.name, async (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case "organizationCreated":
if (this.organizations.length < 1) {
this.organizations = await this.buildOrganizations();
}
break;
}
});
});
}
async buildOrganizations() {
const allOrgs = await this.organizationService.getAll();
return allOrgs
.filter((org) => OrgNavigationPermissionsService.canAccessAdmin(org))
.sort(Utils.getSortFunction(this.i18nService, "name"));
}
lock() {

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