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

Compare commits

..

42 Commits

Author SHA1 Message Date
github-actions[bot]
b7cee309ba Bumped version to 2022.5.2 (#1730)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit ee4afdd0f2)
2022-06-07 13:05:47 -07:00
Daniel James Smith
d886a056fa Fix expanding/collapsing of nested collections (#1722) 2022-06-07 16:04:09 -04:00
github-actions[bot]
06227f27ab Bumped version to 2022.5.1 (#1727)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit 6da0925374)
2022-06-07 11:49:51 -07:00
Addison Beck
66391b4376 [fix] Pull org admin vault collections directly from the API (#1700) (#1726)
* [fix] Pull org admin vault collections directly from the API

* [refactor] Establish pattern for interfaces in module scoped services

* [fix] Remove access modifiers from constructor params for an extended service

* [fix] Rename skipFilterCollectionRefresh to firstLoaded

* [fix] Simplify hiding unwanted filters in the org vault

* Revert "[refactor] Establish pattern for interfaces in module scoped services"

This reverts commit f1edae5a3e.

* Update src/app/modules/vault-filter/vault-filter.component.ts

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

* Update src/app/modules/vault-filter/organization-vault-filter.component.ts

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

* [fix] Ran prettier

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

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2022-06-06 13:42:57 -04:00
Kyle Spearrin
1c09629bbc Allow firefox relay service in generator (#1723)
* Allow firefox relay service in generator

* Update generator.component.ts

* Update generator.component.ts

* make pretty
2022-06-02 14:01:33 -04:00
Jake Fink
e2d9498167 add better error handling to tax call (#1666) 2022-06-02 10:02:31 -04:00
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
28 changed files with 215 additions and 245 deletions

View File

@@ -19,8 +19,8 @@ jobs:
name: Setup name: Setup
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
outputs: outputs:
release_version: ${{ steps.version.outputs.package }} release_version: ${{ steps.version.outputs.version }}
tag_version: ${{ steps.version.outputs.tag }} tag_version: ${{ steps.version.outputs.version }}
branch_name: ${{ steps.branch.outputs.branch_name }} branch_name: ${{ steps.branch.outputs.branch_name }}
steps: steps:
- name: Branch check - name: Branch check
@@ -38,20 +38,11 @@ jobs:
- name: Check Release Version - name: Check Release Version
id: version id: version
run: | uses: bitwarden/gh-actions/release-version-check@ea9fab01d76940267b4147cc1c4542431246b9f6
version=$( jq -r ".version" package.json) with:
previous_release_tag_version=$( release-type: ${{ github.event.inputs.release_type }}
curl -sL https://api.github.com/repos/$GITHUB_REPOSITORY/releases/latest | jq -r ".tag_name" project-type: ts
) file: package.json
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"
- name: Get branch name - name: Get branch name
id: branch id: branch

2
jslib

Submodule jslib updated: 3cb94623e2...6f117b9901

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@bitwarden/web-vault", "name": "@bitwarden/web-vault",
"version": "2.28.1", "version": "2022.5.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@bitwarden/web-vault", "name": "@bitwarden/web-vault",
"version": "2.28.1", "version": "2022.5.2",
"hasInstallScript": true, "hasInstallScript": true,
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@bitwarden/web-vault", "name": "@bitwarden/web-vault",
"version": "2.28.1", "version": "2022.5.2",
"license": "GPL-3.0", "license": "GPL-3.0",
"repository": "https://github.com/bitwarden/web", "repository": "https://github.com/bitwarden/web",
"scripts": { "scripts": {

View File

@@ -1,6 +1,6 @@
<div class="container footer text-muted"> <div class="container footer text-muted">
<div class="row"> <div class="row">
<div class="col">&copy; {{ year }} Bitwarden Inc.</div> <div class="col">&copy; {{ year }}, Bitwarden Inc.</div>
<div class="col text-center"></div> <div class="col text-center"></div>
<div class="col text-right"> <div class="col text-right">
{{ "versionNumber" | i18n: version }} {{ "versionNumber" | i18n: version }}

View File

@@ -1,5 +1,5 @@
<router-outlet></router-outlet> <router-outlet></router-outlet>
<div class="container my-5 text-muted text-center"> <div class="container my-5 text-muted text-center">
&copy; {{ year }} Bitwarden Inc. <br /> &copy; {{ year }}, Bitwarden Inc. <br />
{{ "versionNumber" | i18n: version }} {{ "versionNumber" | i18n: version }}
</div> </div>

View File

@@ -35,6 +35,7 @@ import { BulkStatusComponent as OrgBulkStatusComponent } from "../organizations/
import { CollectionAddEditComponent as OrgCollectionAddEditComponent } from "../organizations/manage/collection-add-edit.component"; import { CollectionAddEditComponent as OrgCollectionAddEditComponent } from "../organizations/manage/collection-add-edit.component";
import { CollectionsComponent as OrgManageCollectionsComponent } from "../organizations/manage/collections.component"; import { CollectionsComponent as OrgManageCollectionsComponent } from "../organizations/manage/collections.component";
import { EntityEventsComponent as OrgEntityEventsComponent } from "../organizations/manage/entity-events.component"; import { EntityEventsComponent as OrgEntityEventsComponent } from "../organizations/manage/entity-events.component";
import { EntityUsersComponent as OrgEntityUsersComponent } from "../organizations/manage/entity-users.component";
import { EventsComponent as OrgEventsComponent } from "../organizations/manage/events.component"; import { EventsComponent as OrgEventsComponent } from "../organizations/manage/events.component";
import { GroupAddEditComponent as OrgGroupAddEditComponent } from "../organizations/manage/group-add-edit.component"; import { GroupAddEditComponent as OrgGroupAddEditComponent } from "../organizations/manage/group-add-edit.component";
import { GroupsComponent as OrgGroupsComponent } from "../organizations/manage/groups.component"; import { GroupsComponent as OrgGroupsComponent } from "../organizations/manage/groups.component";
@@ -114,7 +115,6 @@ import { EmergencyAccessTakeoverComponent } from "../settings/emergency-access-t
import { EmergencyAccessViewComponent } from "../settings/emergency-access-view.component"; import { EmergencyAccessViewComponent } from "../settings/emergency-access-view.component";
import { EmergencyAccessComponent } from "../settings/emergency-access.component"; import { EmergencyAccessComponent } from "../settings/emergency-access.component";
import { EmergencyAddEditComponent } from "../settings/emergency-add-edit.component"; import { EmergencyAddEditComponent } from "../settings/emergency-add-edit.component";
import { LinkSsoComponent } from "../settings/link-sso.component";
import { OrganizationPlansComponent } from "../settings/organization-plans.component"; import { OrganizationPlansComponent } from "../settings/organization-plans.component";
import { PaymentMethodComponent } from "../settings/payment-method.component"; import { PaymentMethodComponent } from "../settings/payment-method.component";
import { PaymentComponent } from "../settings/payment.component"; import { PaymentComponent } from "../settings/payment.component";
@@ -223,7 +223,6 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
HintComponent, HintComponent,
ImportComponent, ImportComponent,
InactiveTwoFactorReportComponent, InactiveTwoFactorReportComponent,
LinkSsoComponent,
LockComponent, LockComponent,
LoginComponent, LoginComponent,
MasterPasswordPolicyComponent, MasterPasswordPolicyComponent,
@@ -244,6 +243,7 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
OrgCollectionAddEditComponent, OrgCollectionAddEditComponent,
OrgCollectionsComponent, OrgCollectionsComponent,
OrgEntityEventsComponent, OrgEntityEventsComponent,
OrgEntityUsersComponent,
OrgEventsComponent, OrgEventsComponent,
OrgExportComponent, OrgExportComponent,
OrgExposedPasswordsReportComponent, OrgExposedPasswordsReportComponent,
@@ -383,7 +383,6 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
HintComponent, HintComponent,
ImportComponent, ImportComponent,
InactiveTwoFactorReportComponent, InactiveTwoFactorReportComponent,
LinkSsoComponent,
LockComponent, LockComponent,
LoginComponent, LoginComponent,
MasterPasswordPolicyComponent, MasterPasswordPolicyComponent,
@@ -404,6 +403,7 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
OrgCollectionAddEditComponent, OrgCollectionAddEditComponent,
OrgCollectionsComponent, OrgCollectionsComponent,
OrgEntityEventsComponent, OrgEntityEventsComponent,
OrgEntityUsersComponent,
OrgEventsComponent, OrgEventsComponent,
OrgExportComponent, OrgExportComponent,
OrgExposedPasswordsReportComponent, OrgExposedPasswordsReportComponent,

View File

@@ -1,13 +0,0 @@
import { ScrollingModule } from "@angular/cdk/scrolling";
import { NgModule } from "@angular/core";
import { SharedModule } from "../../shared.module";
import { EntityUsersComponent } from "./entity-users.component";
@NgModule({
imports: [SharedModule, ScrollingModule],
declarations: [EntityUsersComponent],
exports: [EntityUsersComponent],
})
export class OrganizationManageModule {}

View File

@@ -31,7 +31,7 @@
<button <button
class="toggle-button" class="toggle-button"
*ngIf="c.children.length" *ngIf="c.children.length"
(click)="collapse(c.node)" (click)="toggleCollapse(c.node)"
title="{{ 'toggleCollapse' | i18n }}" title="{{ 'toggleCollapse' | i18n }}"
[attr.aria-expanded]="!isCollapsed(c.node)" [attr.aria-expanded]="!isCollapsed(c.node)"
[attr.aria-controls]="c.node.name + '_children'" [attr.aria-controls]="c.node.name + '_children'"

View File

@@ -14,7 +14,7 @@
<span class="filter-buttons"> <span class="filter-buttons">
<a href="#" routerLink="/create-organization" class="filter-button"> <a href="#" routerLink="/create-organization" class="filter-button">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i> <i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
{{ "newOrganization" | i18n }} &nbsp;{{ "newOrganization" | i18n }}
</a> </a>
</span> </span>
</li> </li>
@@ -45,14 +45,6 @@
> >
&nbsp;{{ organizationGrouping.name | i18n }} &nbsp;{{ organizationGrouping.name | i18n }}
</button> </button>
<a
href="#"
routerLink="/create-organization"
class="text-muted ml-auto create-organization-link"
appA11yTitle="{{ 'newOrganization' | i18n }}"
>
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
</a>
</div> </div>
<ul id="organization-filters" *ngIf="!isCollapsed" class="filter-options"> <ul id="organization-filters" *ngIf="!isCollapsed" class="filter-options">
<li <li
@@ -75,6 +67,14 @@
</ng-container> </ng-container>
</span> </span>
</li> </li>
<li class="filter-option">
<span class="filter-buttons">
<a href="#" routerLink="/create-organization" class="filter-button">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
&nbsp;{{ "newOrganization" | i18n }}
</a>
</span>
</li>
</ul> </ul>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'singleOrganizationAndPersonalOwnershipPolicies'"> <ng-container *ngSwitchCase="'singleOrganizationAndPersonalOwnershipPolicies'">
@@ -110,15 +110,6 @@
> >
&nbsp;{{ organizationGrouping.name | i18n }} &nbsp;{{ organizationGrouping.name | i18n }}
</button> </button>
<a
href="#"
routerLink="/create-organization"
class="text-muted ml-auto create-organization-link"
appA11yTitle="{{ 'newOrganization' | i18n }}"
*ngIf="!(displayMode === 'singleOrganizationPolicy')"
>
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
</a>
</div> </div>
<ul id="organization-filters" *ngIf="!isCollapsed" class="filter-options"> <ul id="organization-filters" *ngIf="!isCollapsed" class="filter-options">
<li class="filter-option" [ngClass]="{ active: activeFilter.myVaultOnly }"> <li class="filter-option" [ngClass]="{ active: activeFilter.myVaultOnly }">
@@ -149,6 +140,14 @@
</ng-container> </ng-container>
</span> </span>
</li> </li>
<li class="filter-option" *ngIf="!(displayMode === 'singleOrganizationPolicy')">
<span class="filter-buttons">
<a href="#" routerLink="/create-organization" class="filter-button">
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
&nbsp;{{ "newOrganization" | i18n }}
</a>
</span>
</li>
</ul> </ul>
</ng-container> </ng-container>
</ng-container> </ng-container>

View File

@@ -0,0 +1,28 @@
import { Component, Input } from "@angular/core";
import { Organization } from "jslib-common/models/domain/organization";
import { VaultFilterComponent } from "./vault-filter.component";
@Component({
selector: "app-organization-vault-filter",
templateUrl: "vault-filter.component.html",
})
export class OrganizationVaultFilterComponent extends VaultFilterComponent {
hideOrganizations = true;
hideFavorites = true;
hideFolders = true;
organization: Organization;
async initCollections() {
if (this.organization.canEditAnyCollection) {
return await this.vaultFilterService.buildAdminCollections(this.organization.id);
}
return await this.vaultFilterService.buildCollections(this.organization.id);
}
async reloadCollectionsAndFolders() {
this.collections = await this.initCollections();
}
}

View File

@@ -27,7 +27,6 @@
appAutofocus appAutofocus
/> />
<app-organization-filter <app-organization-filter
*ngIf="showOrgFilter"
[hide]="hideOrganizations" [hide]="hideOrganizations"
[activeFilter]="activeFilter" [activeFilter]="activeFilter"
[collapsedFilterNodes]="collapsedFilterNodes" [collapsedFilterNodes]="collapsedFilterNodes"
@@ -39,7 +38,7 @@
></app-organization-filter> ></app-organization-filter>
<div class="filter"> <div class="filter">
<app-status-filter <app-status-filter
[hideFavorites]="!showFavorites" [hideFavorites]="hideFavorites"
[hideTrash]="hideTrash" [hideTrash]="hideTrash"
[activeFilter]="activeFilter" [activeFilter]="activeFilter"
(onFilterChange)="applyFilter($event)" (onFilterChange)="applyFilter($event)"
@@ -55,7 +54,7 @@
</div> </div>
<div class="filter"> <div class="filter">
<app-folder-filter <app-folder-filter
[hide]="!showFolders" [hide]="hideFolders"
[activeFilter]="activeFilter" [activeFilter]="activeFilter"
[collapsedFilterNodes]="collapsedFilterNodes" [collapsedFilterNodes]="collapsedFilterNodes"
[folderNodes]="folders" [folderNodes]="folders"

View File

@@ -1,26 +1,22 @@
import { Component, EventEmitter, Input, Output } from "@angular/core"; import { Component, EventEmitter, Output } from "@angular/core";
import { VaultFilterComponent as BaseVaultFilterComponent } from "jslib-angular/modules/vault-filter/vault-filter.component"; import { VaultFilterComponent as BaseVaultFilterComponent } from "jslib-angular/modules/vault-filter/vault-filter.component";
import { VaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service";
import { Organization } from "jslib-common/models/domain/organization"; import { VaultFilterService } from "./vault-filter.service";
@Component({ @Component({
selector: "app-vault-filter", selector: "app-vault-filter",
templateUrl: "vault-filter.component.html", templateUrl: "vault-filter.component.html",
}) })
export class VaultFilterComponent extends BaseVaultFilterComponent { export class VaultFilterComponent extends BaseVaultFilterComponent {
@Input() showOrgFilter = true;
@Input() showFolders = true;
@Input() showFavorites = true;
@Output() onSearchTextChanged = new EventEmitter<string>(); @Output() onSearchTextChanged = new EventEmitter<string>();
searchPlaceholder: string; searchPlaceholder: string;
searchText = ""; searchText = "";
organization: Organization; constructor(protected vaultFilterService: VaultFilterService) {
// This empty constructor is required to provide the web vaultFilterService subclass to super()
constructor(vaultFilterService: VaultFilterService) { // TODO: refactor this to use proper dependency injection
super(vaultFilterService); super(vaultFilterService);
} }
@@ -28,7 +24,13 @@ export class VaultFilterComponent extends BaseVaultFilterComponent {
this.onSearchTextChanged.emit(this.searchText); this.onSearchTextChanged.emit(this.searchText);
} }
async initCollections() { // This method exists because the vault component gets its data mixed up during the initial sync on first login. It looks for data before the sync is complete.
return await this.vaultFilterService.buildCollections(this.organization?.id); // It should be removed as soon as doing so makes sense.
async reloadOrganizations() {
this.organizations = await this.vaultFilterService.buildOrganizations();
this.activePersonalOwnershipPolicy =
await this.vaultFilterService.checkForPersonalOwnershipPolicy();
this.activeSingleOrganizationPolicy =
await this.vaultFilterService.checkForSingleOrganizationPolicy();
} }
} }

View File

@@ -1,22 +1,17 @@
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { VaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service";
import { CipherService } from "jslib-common/abstractions/cipher.service";
import { CollectionService } from "jslib-common/abstractions/collection.service";
import { FolderService } from "jslib-common/abstractions/folder.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { SharedModule } from "../shared.module"; import { SharedModule } from "../shared.module";
import { CollectionFilterComponent } from "./components/collection-filter.component"; import { CollectionFilterComponent } from "./components/collection-filter.component";
import { FolderFilterComponent } from "./components/folder-filter.component"; import { FolderFilterComponent } from "./components/folder-filter.component";
import { LinkSsoComponent } from "./components/link-sso.component";
import { OrganizationFilterComponent } from "./components/organization-filter.component"; import { OrganizationFilterComponent } from "./components/organization-filter.component";
import { OrganizationOptionsComponent } from "./components/organization-options.component"; import { OrganizationOptionsComponent } from "./components/organization-options.component";
import { StatusFilterComponent } from "./components/status-filter.component"; import { StatusFilterComponent } from "./components/status-filter.component";
import { TypeFilterComponent } from "./components/type-filter.component"; import { TypeFilterComponent } from "./components/type-filter.component";
import { OrganizationVaultFilterComponent } from "./organization-vault-filter.component";
import { VaultFilterComponent } from "./vault-filter.component"; import { VaultFilterComponent } from "./vault-filter.component";
import { VaultFilterService } from "./vault-filter.service";
@NgModule({ @NgModule({
imports: [SharedModule], imports: [SharedModule],
@@ -28,21 +23,10 @@ import { VaultFilterComponent } from "./vault-filter.component";
OrganizationOptionsComponent, OrganizationOptionsComponent,
StatusFilterComponent, StatusFilterComponent,
TypeFilterComponent, TypeFilterComponent,
OrganizationVaultFilterComponent,
LinkSsoComponent,
], ],
exports: [VaultFilterComponent], exports: [VaultFilterComponent, OrganizationVaultFilterComponent],
providers: [ providers: [VaultFilterService],
{
provide: VaultFilterService,
useClass: VaultFilterService,
deps: [
StateService,
OrganizationService,
FolderService,
CipherService,
CollectionService,
PolicyService,
],
},
],
}) })
export class VaultFilterModule {} export class VaultFilterModule {}

View File

@@ -1,3 +1,54 @@
import { VaultFilterService as BaseVaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service"; import { Injectable } from "@angular/core";
export class VaultFilterService extends BaseVaultFilterService {} import { DynamicTreeNode } from "jslib-angular/modules/vault-filter/models/dynamic-tree-node.model";
import { VaultFilterService as BaseVaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service";
import { ApiService } from "jslib-common/abstractions/api.service";
import { CipherService } from "jslib-common/abstractions/cipher.service";
import { CollectionService } from "jslib-common/abstractions/collection.service";
import { FolderService } from "jslib-common/abstractions/folder.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PolicyService } from "jslib-common/abstractions/policy.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { CollectionData } from "jslib-common/models/data/collectionData";
import { Collection } from "jslib-common/models/domain/collection";
import { CollectionDetailsResponse } from "jslib-common/models/response/collectionResponse";
import { CollectionView } from "jslib-common/models/view/collectionView";
@Injectable()
export class VaultFilterService extends BaseVaultFilterService {
constructor(
stateService: StateService,
organizationService: OrganizationService,
folderService: FolderService,
cipherService: CipherService,
collectionService: CollectionService,
policyService: PolicyService,
protected apiService: ApiService
) {
super(
stateService,
organizationService,
folderService,
cipherService,
collectionService,
policyService
);
}
async buildAdminCollections(organizationId: string) {
let result: CollectionView[] = [];
const collectionResponse = await this.apiService.getCollections(organizationId);
if (collectionResponse?.data != null && collectionResponse.data.length) {
const collectionDomains = collectionResponse.data.map(
(r: CollectionDetailsResponse) => new Collection(new CollectionData(r))
);
result = await this.collectionService.decryptMany(collectionDomains);
}
const nestedCollections = await this.collectionService.getAllNested(result);
return new DynamicTreeNode<CollectionView>({
fullList: result,
nestedList: nestedCollections,
});
}
}

View File

@@ -4,15 +4,12 @@
<div class="groupings"> <div class="groupings">
<div class="content"> <div class="content">
<div class="inner-content"> <div class="inner-content">
<app-vault-filter <app-organization-vault-filter
#vaultFilter #vaultFilter
[showFolders]="false"
[showFavorites]="false"
[activeFilter]="activeFilter" [activeFilter]="activeFilter"
[showOrgFilter]="false"
(onFilterChange)="applyVaultFilter($event)" (onFilterChange)="applyVaultFilter($event)"
(onSearchTextChanged)="filterSearchText($event)" (onSearchTextChanged)="filterSearchText($event)"
></app-vault-filter> ></app-organization-vault-filter>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -29,7 +29,7 @@ import { AddEditComponent } from "../../../../organizations/vault/add-edit.compo
import { AttachmentsComponent } from "../../../../organizations/vault/attachments.component"; import { AttachmentsComponent } from "../../../../organizations/vault/attachments.component";
import { CiphersComponent } from "../../../../organizations/vault/ciphers.component"; import { CiphersComponent } from "../../../../organizations/vault/ciphers.component";
import { CollectionsComponent } from "../../../../organizations/vault/collections.component"; import { CollectionsComponent } from "../../../../organizations/vault/collections.component";
import { VaultFilterComponent } from "../../../vault-filter/vault-filter.component"; import { OrganizationVaultFilterComponent } from "../../../vault-filter/organization-vault-filter.component";
import { VaultService } from "../../vault.service"; import { VaultService } from "../../vault.service";
const BroadcasterSubscriptionId = "OrgVaultComponent"; const BroadcasterSubscriptionId = "OrgVaultComponent";
@@ -39,7 +39,8 @@ const BroadcasterSubscriptionId = "OrgVaultComponent";
templateUrl: "organization-vault.component.html", templateUrl: "organization-vault.component.html",
}) })
export class OrganizationVaultComponent implements OnInit, OnDestroy { export class OrganizationVaultComponent implements OnInit, OnDestroy {
@ViewChild("vaultFilter", { static: true }) vaultFilterComponent: VaultFilterComponent; @ViewChild("vaultFilter", { static: true })
vaultFilterComponent: OrganizationVaultFilterComponent;
@ViewChild(CiphersComponent, { static: true }) ciphersComponent: CiphersComponent; @ViewChild(CiphersComponent, { static: true }) ciphersComponent: CiphersComponent;
@ViewChild("attachments", { read: ViewContainerRef, static: true }) @ViewChild("attachments", { read: ViewContainerRef, static: true })
attachmentsModalRef: ViewContainerRef; attachmentsModalRef: ViewContainerRef;
@@ -57,6 +58,11 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy {
trashCleanupWarning: string = null; trashCleanupWarning: string = null;
activeFilter: VaultFilter = new VaultFilter(); activeFilter: VaultFilter = new VaultFilter();
// This is a hack to avoid redundant api calls that fetch OrganizationVaultFilterComponent collections
// When it makes sense to do so we should leverage some other communication method for change events that isn't directly tied to the query param for organizationId
// i.e. exposing the VaultFiltersService to the OrganizationSwitcherComponent to make relevant updates from a change event instead of just depending on the router
firstLoaded = true;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private organizationService: OrganizationService, private organizationService: OrganizationService,
@@ -95,11 +101,7 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy {
case "syncCompleted": case "syncCompleted":
if (message.successfully) { if (message.successfully) {
await Promise.all([ await Promise.all([
this.vaultFilterComponent.reloadCollectionsAndFolders( this.vaultFilterComponent.reloadCollectionsAndFolders(),
new VaultFilter({
selectedOrganizationId: this.organization.id,
} as Partial<VaultFilter>)
),
this.ciphersComponent.refresh(), this.ciphersComponent.refresh(),
]); ]);
this.changeDetectorRef.detectChanges(); this.changeDetectorRef.detectChanges();
@@ -109,9 +111,12 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy {
}); });
}); });
} }
await this.vaultFilterComponent.reloadCollectionsAndFolders(
new VaultFilter({ selectedOrganizationId: this.organization.id } as Partial<VaultFilter>) if (!this.firstLoaded) {
); await this.vaultFilterComponent.reloadCollectionsAndFolders();
}
this.firstLoaded = false;
await this.ciphersComponent.reload(); await this.ciphersComponent.reload();
if (qParams.viewEvents != null) { if (qParams.viewEvents != null) {

View File

@@ -20,9 +20,8 @@ import {
import { ListResponse } from "jslib-common/models/response/listResponse"; import { ListResponse } from "jslib-common/models/response/listResponse";
import { CollectionView } from "jslib-common/models/view/collectionView"; import { CollectionView } from "jslib-common/models/view/collectionView";
import { EntityUsersComponent } from "../../modules/organizations/manage/entity-users.component";
import { CollectionAddEditComponent } from "./collection-add-edit.component"; import { CollectionAddEditComponent } from "./collection-add-edit.component";
import { EntityUsersComponent } from "./entity-users.component";
@Component({ @Component({
selector: "app-org-manage-collections", selector: "app-org-manage-collections",

View File

@@ -29,52 +29,52 @@
></i> ></i>
<span class="sr-only">{{ "loading" | i18n }}</span> <span class="sr-only">{{ "loading" | i18n }}</span>
</div> </div>
<cdk-virtual-scroll-viewport <div
itemSize="46" class="modal-body"
minBufferPx="600" *ngIf="
maxBufferPx="1200" !loading && users && (users | search: searchText:'name':'email':'id') as searchedUsers
[style]="scrollViewportStyle" "
> >
<div class="modal-body" *ngIf="!loading && users && searchedUsers"> <div class="d-flex">
<div class="d-flex"> <div class="mr-3">
<div class="mr-3"> <label class="sr-only" for="search">{{ "search" | i18n }}</label>
<label class="sr-only" for="search">{{ "search" | i18n }}</label> <input
<input type="search"
type="search" class="form-control form-control-sm"
class="form-control form-control-sm" id="search"
id="search" placeholder="{{ 'search' | i18n }}"
placeholder="{{ 'search' | i18n }}" name="SearchText"
name="SearchText" [(ngModel)]="searchText"
[(ngModel)]="searchText" />
/>
</div>
<div class="btn-group btn-group-sm" role="group">
<button
type="button"
class="btn btn-outline-secondary"
[ngClass]="{ active: !showSelected }"
(click)="filterSelected(false)"
>
{{ "all" | i18n }}
</button>
<button
type="button"
class="btn btn-outline-secondary"
[ngClass]="{ active: showSelected }"
(click)="filterSelected(true)"
>
{{ "selected" | i18n }}
<span class="badge badge-pill badge-info" *ngIf="selectedCount">{{
selectedCount
}}</span>
</button>
</div>
</div> </div>
<ng-container *ngIf="!searchedUsers.length"> <div class="btn-group btn-group-sm" role="group">
<hr /> <button
{{ "noUsersInList" | i18n }} type="button"
</ng-container> class="btn btn-outline-secondary"
<table class="table table-hover table-list mb-0" [hidden]="!searchedUsers.length"> [ngClass]="{ active: !showSelected }"
(click)="filterSelected(false)"
>
{{ "all" | i18n }}
</button>
<button
type="button"
class="btn btn-outline-secondary"
[ngClass]="{ active: showSelected }"
(click)="filterSelected(true)"
>
{{ "selected" | i18n }}
<span class="badge badge-pill badge-info" *ngIf="selectedCount">{{
selectedCount
}}</span>
</button>
</div>
</div>
<ng-container *ngIf="!searchedUsers.length">
<hr />
{{ "noUsersInList" | i18n }}
</ng-container>
<ng-container *ngIf="searchedUsers.length">
<table class="table table-hover table-list mb-0">
<thead> <thead>
<tr> <tr>
<th>&nbsp;</th> <th>&nbsp;</th>
@@ -91,7 +91,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *cdkVirtualFor="let u of searchedUsers" class=""> <tr *ngFor="let u of searchedUsers">
<td class="table-list-checkbox" (click)="check(u)"> <td class="table-list-checkbox" (click)="check(u)">
<input <input
type="checkbox" type="checkbox"
@@ -164,8 +164,8 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </ng-container>
</cdk-virtual-scroll-viewport> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading"> <button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i> <i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>

View File

@@ -1,6 +1,5 @@
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
@@ -14,7 +13,6 @@ import { OrganizationUserUserDetailsResponse } from "jslib-common/models/respons
@Component({ @Component({
selector: "app-entity-users", selector: "app-entity-users",
templateUrl: "entity-users.component.html", templateUrl: "entity-users.component.html",
providers: [SearchPipe],
}) })
export class EntityUsersComponent implements OnInit { export class EntityUsersComponent implements OnInit {
@Input() entity: "group" | "collection"; @Input() entity: "group" | "collection";
@@ -35,7 +33,6 @@ export class EntityUsersComponent implements OnInit {
private allUsers: OrganizationUserUserDetailsResponse[] = []; private allUsers: OrganizationUserUserDetailsResponse[] = [];
constructor( constructor(
private search: SearchPipe,
private apiService: ApiService, private apiService: ApiService,
private i18nService: I18nService, private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
@@ -55,14 +52,6 @@ export class EntityUsersComponent implements OnInit {
} }
} }
get searchedUsers() {
return this.search.transform(this.users, this.searchText, "name", "email", "id");
}
get scrollViewportStyle() {
return `min-height: 120px; height: ${120 + this.searchedUsers.length * 46}px`;
}
async loadUsers() { async loadUsers() {
const users = await this.apiService.getOrganizationUsers(this.organizationId); const users = await this.apiService.getOrganizationUsers(this.organizationId);
this.allUsers = users.data.map((r) => r).sort(Utils.getSortFunction(this.i18nService, "email")); this.allUsers = users.data.map((r) => r).sort(Utils.getSortFunction(this.i18nService, "email"));

View File

@@ -12,8 +12,7 @@ import { SearchService } from "jslib-common/abstractions/search.service";
import { Utils } from "jslib-common/misc/utils"; import { Utils } from "jslib-common/misc/utils";
import { GroupResponse } from "jslib-common/models/response/groupResponse"; import { GroupResponse } from "jslib-common/models/response/groupResponse";
import { EntityUsersComponent } from "../../modules/organizations/manage/entity-users.component"; import { EntityUsersComponent } from "./entity-users.component";
import { GroupAddEditComponent } from "./group-add-edit.component"; import { GroupAddEditComponent } from "./group-add-edit.component";
@Component({ @Component({

View File

@@ -1,7 +1,6 @@
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { LooseComponentsModule } from "./modules/loose-components.module"; import { LooseComponentsModule } from "./modules/loose-components.module";
import { OrganizationManageModule } from "./modules/organizations/manage/organization-manage.module";
import { PipesModule } from "./modules/pipes/pipes.module"; import { PipesModule } from "./modules/pipes/pipes.module";
import { SharedModule } from "./modules/shared.module"; import { SharedModule } from "./modules/shared.module";
import { VaultFilterModule } from "./modules/vault-filter/vault-filter.module"; import { VaultFilterModule } from "./modules/vault-filter/vault-filter.module";
@@ -14,7 +13,6 @@ import { OrganizationBadgeModule } from "./modules/vault/modules/organization-ba
VaultFilterModule, VaultFilterModule,
OrganizationBadgeModule, OrganizationBadgeModule,
PipesModule, PipesModule,
OrganizationManageModule,
], ],
exports: [LooseComponentsModule, VaultFilterModule, OrganizationBadgeModule, PipesModule], exports: [LooseComponentsModule, VaultFilterModule, OrganizationBadgeModule, PipesModule],
bootstrap: [], bootstrap: [],

View File

@@ -40,11 +40,13 @@ export class GeneratorComponent extends BaseGeneratorComponent {
route, route,
window window
); );
// Cannot use Firefox Relay on the web vault yet due to CORS issues with Firefox Relay API if (platformUtilsService.isSelfHost()) {
this.forwardOptions.splice( // Cannot use Firefox Relay on self hosted web vaults due to CORS issues with Firefox Relay API
this.forwardOptions.findIndex((o) => o.value === "firefoxrelay"), this.forwardOptions.splice(
1 this.forwardOptions.findIndex((o) => o.value === "firefoxrelay"),
); 1
);
}
} }
async history() { async history() {

View File

@@ -4674,8 +4674,8 @@
"removeSponsorshipSuccess": { "removeSponsorshipSuccess": {
"message": "Sponsorship Removed" "message": "Sponsorship Removed"
}, },
"ssoKeyConnectorUnavailable": { "ssoKeyConnectorError": {
"message": "Unable to reach the Key Connector, try again later." "message": "Key Connector error: make sure Key Connector is available and working correctly."
}, },
"keyConnectorUrl": { "keyConnectorUrl": {
"message": "Key Connector URL" "message": "Key Connector URL"

View File

@@ -14,14 +14,6 @@
font-size: $font-size-base; font-size: $font-size-base;
} }
a.create-organization-link {
&:hover {
@include themify($themes) {
color: themed("iconHover") !important;
}
}
}
button { button {
@extend .no-btn; @extend .no-btn;
} }

View File

@@ -204,60 +204,8 @@ const devServer =
return [ return [
{ {
key: "Content-Security-Policy", key: "Content-Security-Policy",
value: ` value:
default-src 'self'; "default-src 'self'; script-src 'self' 'sha256-ryoU+5+IUZTuUyTElqkrQGBJXr1brEv6r2CA62WUw8w=' https://js.stripe.com https://js.braintreegateway.com https://www.paypalobjects.com; style-src 'self' https://assets.braintreegateway.com https://*.paypal.com 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' 'sha256-JVRXyYPueLWdwGwY9m/7u4QlZ1xeQdqUj2t8OVIzZE4='; img-src 'self' data: https://icons.bitwarden.net https://*.paypal.com https://www.paypalobjects.com https://q.stripe.com https://haveibeenpwned.com https://www.gravatar.com; child-src 'self' https://js.stripe.com https://assets.braintreegateway.com https://*.paypal.com https://*.duosecurity.com; frame-src 'self' https://js.stripe.com https://assets.braintreegateway.com https://*.paypal.com https://*.duosecurity.com; connect-src 'self' wss://notifications.bitwarden.com https://notifications.bitwarden.com https://cdn.bitwarden.net https://api.pwnedpasswords.com https://2fa.directory/api/v3/totp.json https://api.stripe.com https://www.paypal.com https://api.braintreegateway.com https://client-analytics.braintreegateway.com https://*.braintree-api.com https://*.blob.core.windows.net https://app.simplelogin.io/api/alias/random/new https://app.anonaddy.com/api/v1/aliases; object-src 'self' blob:;",
script-src
'self'
'sha256-ryoU+5+IUZTuUyTElqkrQGBJXr1brEv6r2CA62WUw8w='
https://js.stripe.com
https://js.braintreegateway.com
https://www.paypalobjects.com;
style-src
'self'
https://assets.braintreegateway.com
https://*.paypal.com
'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='
'sha256-JVRXyYPueLWdwGwY9m/7u4QlZ1xeQdqUj2t8OVIzZE4=';
'sha256-0xHKHIT3+e2Gknxsm/cpErSprhL+o254L/y5bljg74U='
img-src
'self'
data:
https://icons.bitwarden.net
https://*.paypal.com
https://www.paypalobjects.com
https://q.stripe.com
https://haveibeenpwned.com
https://www.gravatar.com;
child-src
'self'
https://js.stripe.com
https://assets.braintreegateway.com
https://*.paypal.com
https://*.duosecurity.com;
frame-src
'self'
https://js.stripe.com
https://assets.braintreegateway.com
https://*.paypal.com
https://*.duosecurity.com;
connect-src
'self'
wss://notifications.bitwarden.com
https://notifications.bitwarden.com
https://cdn.bitwarden.net
https://api.pwnedpasswords.com
https://2fa.directory/api/v3/totp.json
https://api.stripe.com
https://www.paypal.com
https://api.braintreegateway.com
https://client-analytics.braintreegateway.com
https://*.braintree-api.com
https://*.blob.core.windows.net
https://app.simplelogin.io/api/alias/random/new
https://app.anonaddy.com/api/v1/aliases;
object-src
'self'
blob:;`,
}, },
]; ];
} }