mirror of
https://github.com/bitwarden/web
synced 2025-12-17 08:43:14 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7cee309ba | ||
|
|
d886a056fa | ||
|
|
06227f27ab | ||
|
|
66391b4376 | ||
|
|
1c09629bbc | ||
|
|
e2d9498167 |
2
jslib
2
jslib
Submodule jslib updated: 1cc4bed671...6f117b9901
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/web-vault",
|
"name": "@bitwarden/web-vault",
|
||||||
"version": "2022.05.0",
|
"version": "2022.5.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@bitwarden/web-vault",
|
"name": "@bitwarden/web-vault",
|
||||||
"version": "2022.05.0",
|
"version": "2022.5.2",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/web-vault",
|
"name": "@bitwarden/web-vault",
|
||||||
"version": "2022.05.0",
|
"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": {
|
||||||
|
|||||||
@@ -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'"
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +33,4 @@ export class VaultFilterComponent extends BaseVaultFilterComponent {
|
|||||||
this.activeSingleOrganizationPolicy =
|
this.activeSingleOrganizationPolicy =
|
||||||
await this.vaultFilterService.checkForSingleOrganizationPolicy();
|
await this.vaultFilterService.checkForSingleOrganizationPolicy();
|
||||||
}
|
}
|
||||||
|
|
||||||
async initCollections() {
|
|
||||||
return await this.vaultFilterService.buildCollections(this.organization?.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
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";
|
||||||
@@ -17,7 +9,9 @@ import { OrganizationFilterComponent } from "./components/organization-filter.co
|
|||||||
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],
|
||||||
@@ -29,22 +23,10 @@ import { VaultFilterComponent } from "./vault-filter.component";
|
|||||||
OrganizationOptionsComponent,
|
OrganizationOptionsComponent,
|
||||||
StatusFilterComponent,
|
StatusFilterComponent,
|
||||||
TypeFilterComponent,
|
TypeFilterComponent,
|
||||||
|
OrganizationVaultFilterComponent,
|
||||||
LinkSsoComponent,
|
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 {}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -73,10 +73,14 @@ export class TaxInfoComponent {
|
|||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const taxInfo = await this.apiService.getTaxInfo();
|
try {
|
||||||
if (taxInfo) {
|
const taxInfo = await this.apiService.getTaxInfo();
|
||||||
this.taxInfo.postalCode = taxInfo.postalCode;
|
if (taxInfo) {
|
||||||
this.taxInfo.country = taxInfo.country || "US";
|
this.taxInfo.postalCode = taxInfo.postalCode;
|
||||||
|
this.taxInfo.country = taxInfo.country || "US";
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.pristine = Object.assign({}, this.taxInfo);
|
this.pristine = Object.assign({}, this.taxInfo);
|
||||||
@@ -86,9 +90,16 @@ export class TaxInfoComponent {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const taxRates = await this.apiService.getTaxRates();
|
try {
|
||||||
this.taxRates = taxRates.data;
|
const taxRates = await this.apiService.getTaxRates();
|
||||||
this.loading = false;
|
if (taxRates) {
|
||||||
|
this.taxRates = taxRates.data;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(e);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get taxRate() {
|
get taxRate() {
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user