mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-31 17:57:43 +01:00
[AC-1121] Collections Add Access filter and badge (#8404)
* added bit toggle group for add access filter to AC collections
This commit is contained in:
parent
c051412d41
commit
be51f1934a
@ -20,7 +20,7 @@
|
|||||||
bitLink
|
bitLink
|
||||||
[disabled]="disabled"
|
[disabled]="disabled"
|
||||||
type="button"
|
type="button"
|
||||||
class="tw-w-full tw-truncate tw-text-start tw-leading-snug"
|
class="tw-flex tw-w-full tw-text-start tw-leading-snug"
|
||||||
linkType="secondary"
|
linkType="secondary"
|
||||||
title="{{ 'viewCollectionWithName' | i18n: collection.name }}"
|
title="{{ 'viewCollectionWithName' | i18n: collection.name }}"
|
||||||
[routerLink]="[]"
|
[routerLink]="[]"
|
||||||
@ -28,7 +28,15 @@
|
|||||||
queryParamsHandling="merge"
|
queryParamsHandling="merge"
|
||||||
appStopProp
|
appStopProp
|
||||||
>
|
>
|
||||||
{{ collection.name }}
|
<span class="tw-truncate tw-mr-1">{{ collection.name }}</span>
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
*ngIf="collection.addAccess && collection.id !== Unassigned"
|
||||||
|
bitBadge
|
||||||
|
variant="warning"
|
||||||
|
>{{ "addAccess" | i18n }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td bitCell [ngClass]="RowHeightClass" *ngIf="showOwner">
|
<td bitCell [ngClass]="RowHeightClass" *ngIf="showOwner">
|
||||||
|
@ -21,6 +21,7 @@ import { RowHeightClass } from "./vault-items.component";
|
|||||||
})
|
})
|
||||||
export class VaultCollectionRowComponent {
|
export class VaultCollectionRowComponent {
|
||||||
protected RowHeightClass = RowHeightClass;
|
protected RowHeightClass = RowHeightClass;
|
||||||
|
protected Unassigned = "unassigned";
|
||||||
|
|
||||||
@Input() disabled: boolean;
|
@Input() disabled: boolean;
|
||||||
@Input() collection: CollectionView;
|
@Input() collection: CollectionView;
|
||||||
|
@ -99,8 +99,12 @@
|
|||||||
(checkedToggled)="selection.toggle(item)"
|
(checkedToggled)="selection.toggle(item)"
|
||||||
(onEvent)="event($event)"
|
(onEvent)="event($event)"
|
||||||
></tr>
|
></tr>
|
||||||
|
<!--
|
||||||
|
addAccessStatus check here so ciphers do not show if user
|
||||||
|
has filtered for collections with addAccess
|
||||||
|
-->
|
||||||
<tr
|
<tr
|
||||||
*ngIf="item.cipher"
|
*ngIf="item.cipher && (!addAccessToggle || (addAccessToggle && addAccessStatus !== 1))"
|
||||||
bitRow
|
bitRow
|
||||||
appVaultCipherRow
|
appVaultCipherRow
|
||||||
alignContent="middle"
|
alignContent="middle"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { SelectionModel } from "@angular/cdk/collections";
|
import { SelectionModel } from "@angular/cdk/collections";
|
||||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
|
|
||||||
|
import { OrganizationUserType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||||
@ -45,6 +46,8 @@ export class VaultItemsComponent {
|
|||||||
@Input() showPermissionsColumn = false;
|
@Input() showPermissionsColumn = false;
|
||||||
@Input() viewingOrgVault: boolean;
|
@Input() viewingOrgVault: boolean;
|
||||||
@Input({ required: true }) flexibleCollectionsV1Enabled = false;
|
@Input({ required: true }) flexibleCollectionsV1Enabled = false;
|
||||||
|
@Input() addAccessStatus: number;
|
||||||
|
@Input() addAccessToggle: boolean;
|
||||||
|
|
||||||
private _ciphers?: CipherView[] = [];
|
private _ciphers?: CipherView[] = [];
|
||||||
@Input() get ciphers(): CipherView[] {
|
@Input() get ciphers(): CipherView[] {
|
||||||
@ -101,6 +104,28 @@ export class VaultItemsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const organization = this.allOrganizations.find((o) => o.id === collection.organizationId);
|
const organization = this.allOrganizations.find((o) => o.id === collection.organizationId);
|
||||||
|
|
||||||
|
if (this.flexibleCollectionsV1Enabled) {
|
||||||
|
//Custom user without edit access should not see the Edit option unless that user has "Can Manage" access to a collection
|
||||||
|
if (
|
||||||
|
!collection.manage &&
|
||||||
|
organization?.type === OrganizationUserType.Custom &&
|
||||||
|
!organization?.permissions.editAnyCollection
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//Owner/Admin and Custom Users with Edit can see Edit and Access of Orphaned Collections
|
||||||
|
if (
|
||||||
|
collection.addAccess &&
|
||||||
|
collection.id !== Unassigned &&
|
||||||
|
((organization?.type === OrganizationUserType.Custom &&
|
||||||
|
organization?.permissions.editAnyCollection) ||
|
||||||
|
organization.isAdmin ||
|
||||||
|
organization.isOwner)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return collection.canEdit(organization, this.flexibleCollectionsV1Enabled);
|
return collection.canEdit(organization, this.flexibleCollectionsV1Enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +136,32 @@ export class VaultItemsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const organization = this.allOrganizations.find((o) => o.id === collection.organizationId);
|
const organization = this.allOrganizations.find((o) => o.id === collection.organizationId);
|
||||||
|
|
||||||
|
if (this.flexibleCollectionsV1Enabled) {
|
||||||
|
//Custom user with only edit access should not see the Delete button for orphaned collections
|
||||||
|
if (
|
||||||
|
collection.addAccess &&
|
||||||
|
organization?.type === OrganizationUserType.Custom &&
|
||||||
|
!organization?.permissions.deleteAnyCollection &&
|
||||||
|
organization?.permissions.editAnyCollection
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owner/Admin with no access to a collection will not see Delete
|
||||||
|
if (
|
||||||
|
!collection.assigned &&
|
||||||
|
!collection.addAccess &&
|
||||||
|
(organization.isAdmin || organization.isOwner) &&
|
||||||
|
!(
|
||||||
|
organization?.type === OrganizationUserType.Custom &&
|
||||||
|
organization?.permissions.deleteAnyCollection
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return collection.canDelete(organization);
|
return collection.canDelete(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { CollectionAccessDetailsResponse } from "@bitwarden/common/src/vault/models/response/collection.response";
|
import { CollectionAccessDetailsResponse } from "@bitwarden/common/src/vault/models/response/collection.response";
|
||||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||||
@ -7,6 +8,7 @@ import { CollectionAccessSelectionView } from "../../../admin-console/organizati
|
|||||||
export class CollectionAdminView extends CollectionView {
|
export class CollectionAdminView extends CollectionView {
|
||||||
groups: CollectionAccessSelectionView[] = [];
|
groups: CollectionAccessSelectionView[] = [];
|
||||||
users: CollectionAccessSelectionView[] = [];
|
users: CollectionAccessSelectionView[] = [];
|
||||||
|
addAccess: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag indicating the user has been explicitly assigned to this Collection
|
* Flag indicating the user has been explicitly assigned to this Collection
|
||||||
@ -31,6 +33,33 @@ export class CollectionAdminView extends CollectionView {
|
|||||||
this.assigned = response.assigned;
|
this.assigned = response.assigned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groupsCanManage() {
|
||||||
|
if (this.groups.length === 0) {
|
||||||
|
return this.groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
const returnedGroups = this.groups.filter((group) => {
|
||||||
|
if (group.manage) {
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return returnedGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
usersCanManage(revokedUsers: OrganizationUserUserDetailsResponse[]) {
|
||||||
|
if (this.users.length === 0) {
|
||||||
|
return this.users;
|
||||||
|
}
|
||||||
|
|
||||||
|
const returnedUsers = this.users.filter((user) => {
|
||||||
|
const isRevoked = revokedUsers.some((revoked) => revoked.id === user.id);
|
||||||
|
if (user.manage && !isRevoked) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return returnedUsers;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the current user can edit the collection, including user and group access
|
* Whether the current user can edit the collection, including user and group access
|
||||||
*/
|
*/
|
||||||
|
@ -26,6 +26,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
|
<bit-toggle-group
|
||||||
|
*ngIf="showAddAccessToggle && activeFilter.selectedCollectionNode"
|
||||||
|
[selected]="addAccessStatus$ | async"
|
||||||
|
(selectedChange)="addAccessToggle($event)"
|
||||||
|
[attr.aria-label]="'addAccessFilter' | i18n"
|
||||||
|
>
|
||||||
|
<bit-toggle [value]="0">
|
||||||
|
{{ "all" | i18n }}
|
||||||
|
</bit-toggle>
|
||||||
|
|
||||||
|
<bit-toggle [value]="1">
|
||||||
|
{{ "addAccess" | i18n }}
|
||||||
|
</bit-toggle>
|
||||||
|
</bit-toggle-group>
|
||||||
<app-callout type="warning" *ngIf="activeFilter.isDeleted" icon="bwi bwi-exclamation-triangle">
|
<app-callout type="warning" *ngIf="activeFilter.isDeleted" icon="bwi bwi-exclamation-triangle">
|
||||||
{{ trashCleanupWarning }}
|
{{ trashCleanupWarning }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
@ -54,6 +68,8 @@
|
|||||||
[showBulkAddToCollections]="organization?.flexibleCollections"
|
[showBulkAddToCollections]="organization?.flexibleCollections"
|
||||||
[viewingOrgVault]="true"
|
[viewingOrgVault]="true"
|
||||||
[flexibleCollectionsV1Enabled]="flexibleCollectionsV1Enabled"
|
[flexibleCollectionsV1Enabled]="flexibleCollectionsV1Enabled"
|
||||||
|
[addAccessStatus]="addAccessStatus$ | async"
|
||||||
|
[addAccessToggle]="showAddAccessToggle"
|
||||||
>
|
>
|
||||||
</app-vault-items>
|
</app-vault-items>
|
||||||
<ng-container *ngIf="!flexibleCollectionsV1Enabled">
|
<ng-container *ngIf="!flexibleCollectionsV1Enabled">
|
||||||
|
@ -36,6 +36,9 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|||||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||||
|
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||||
|
import { OrganizationUserType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { EventType } from "@bitwarden/common/enums";
|
import { EventType } from "@bitwarden/common/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
@ -102,6 +105,11 @@ import { VaultFilterComponent } from "./vault-filter/vault-filter.component";
|
|||||||
const BroadcasterSubscriptionId = "OrgVaultComponent";
|
const BroadcasterSubscriptionId = "OrgVaultComponent";
|
||||||
const SearchTextDebounceInterval = 200;
|
const SearchTextDebounceInterval = 200;
|
||||||
|
|
||||||
|
enum AddAccessStatusType {
|
||||||
|
All = 0,
|
||||||
|
AddAccess = 1,
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-org-vault",
|
selector: "app-org-vault",
|
||||||
templateUrl: "vault.component.html",
|
templateUrl: "vault.component.html",
|
||||||
@ -122,6 +130,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
trashCleanupWarning: string = null;
|
trashCleanupWarning: string = null;
|
||||||
activeFilter: VaultFilter = new VaultFilter();
|
activeFilter: VaultFilter = new VaultFilter();
|
||||||
|
|
||||||
|
protected showAddAccessToggle = false;
|
||||||
protected noItemIcon = Icons.Search;
|
protected noItemIcon = Icons.Search;
|
||||||
protected performingInitialLoad = true;
|
protected performingInitialLoad = true;
|
||||||
protected refreshing = false;
|
protected refreshing = false;
|
||||||
@ -149,10 +158,12 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
protected get flexibleCollectionsV1Enabled(): boolean {
|
protected get flexibleCollectionsV1Enabled(): boolean {
|
||||||
return this._flexibleCollectionsV1FlagEnabled && this.organization?.flexibleCollections;
|
return this._flexibleCollectionsV1FlagEnabled && this.organization?.flexibleCollections;
|
||||||
}
|
}
|
||||||
|
protected orgRevokedUsers: OrganizationUserUserDetailsResponse[];
|
||||||
|
|
||||||
private searchText$ = new Subject<string>();
|
private searchText$ = new Subject<string>();
|
||||||
private refresh$ = new BehaviorSubject<void>(null);
|
private refresh$ = new BehaviorSubject<void>(null);
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
protected addAccessStatus$ = new BehaviorSubject<AddAccessStatusType>(0);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -181,6 +192,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
private totpService: TotpService,
|
private totpService: TotpService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private collectionService: CollectionService,
|
private collectionService: CollectionService,
|
||||||
|
private organizationUserService: OrganizationUserService,
|
||||||
protected configService: ConfigService,
|
protected configService: ConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -241,6 +253,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe((activeFilter) => {
|
.subscribe((activeFilter) => {
|
||||||
this.activeFilter = activeFilter;
|
this.activeFilter = activeFilter;
|
||||||
|
|
||||||
|
// watch the active filters. Only show toggle when viewing the collections filter
|
||||||
|
if (!this.activeFilter.collectionId) {
|
||||||
|
this.showAddAccessToggle = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.searchText$
|
this.searchText$
|
||||||
@ -309,6 +326,10 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const allCiphers$ = organization$.pipe(
|
const allCiphers$ = organization$.pipe(
|
||||||
concatMap(async (organization) => {
|
concatMap(async (organization) => {
|
||||||
|
// If user swaps organization reset the addAccessToggle
|
||||||
|
if (!this.showAddAccessToggle || organization) {
|
||||||
|
this.addAccessToggle(0);
|
||||||
|
}
|
||||||
let ciphers;
|
let ciphers;
|
||||||
|
|
||||||
if (this.flexibleCollectionsV1Enabled) {
|
if (this.flexibleCollectionsV1Enabled) {
|
||||||
@ -348,9 +369,21 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const collections$ = combineLatest([nestedCollections$, filter$, this.currentSearchText$]).pipe(
|
// This will be passed into the usersCanManage call
|
||||||
|
this.orgRevokedUsers = (
|
||||||
|
await this.organizationUserService.getAllUsers(await firstValueFrom(organizationId$))
|
||||||
|
).data.filter((user: OrganizationUserUserDetailsResponse) => {
|
||||||
|
return user.status === -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const collections$ = combineLatest([
|
||||||
|
nestedCollections$,
|
||||||
|
filter$,
|
||||||
|
this.currentSearchText$,
|
||||||
|
this.addAccessStatus$,
|
||||||
|
]).pipe(
|
||||||
filter(([collections, filter]) => collections != undefined && filter != undefined),
|
filter(([collections, filter]) => collections != undefined && filter != undefined),
|
||||||
concatMap(async ([collections, filter, searchText]) => {
|
concatMap(async ([collections, filter, searchText, addAccessStatus]) => {
|
||||||
if (
|
if (
|
||||||
filter.collectionId === Unassigned ||
|
filter.collectionId === Unassigned ||
|
||||||
(filter.collectionId === undefined && filter.type !== undefined)
|
(filter.collectionId === undefined && filter.type !== undefined)
|
||||||
@ -358,26 +391,30 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.showAddAccessToggle = false;
|
||||||
let collectionsToReturn = [];
|
let collectionsToReturn = [];
|
||||||
if (filter.collectionId === undefined || filter.collectionId === All) {
|
if (filter.collectionId === undefined || filter.collectionId === All) {
|
||||||
collectionsToReturn = collections.map((c) => c.node);
|
collectionsToReturn = await this.addAccessCollectionsMap(collections);
|
||||||
} else {
|
} else {
|
||||||
const selectedCollection = ServiceUtils.getTreeNodeObjectFromList(
|
const selectedCollection = ServiceUtils.getTreeNodeObjectFromList(
|
||||||
collections,
|
collections,
|
||||||
filter.collectionId,
|
filter.collectionId,
|
||||||
);
|
);
|
||||||
collectionsToReturn = selectedCollection?.children.map((c) => c.node) ?? [];
|
collectionsToReturn = await this.addAccessCollectionsMap(selectedCollection?.children);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await this.searchService.isSearchable(searchText)) {
|
if (await this.searchService.isSearchable(searchText)) {
|
||||||
collectionsToReturn = this.searchPipe.transform(
|
collectionsToReturn = this.searchPipe.transform(
|
||||||
collectionsToReturn,
|
collectionsToReturn,
|
||||||
searchText,
|
searchText,
|
||||||
(collection) => collection.name,
|
(collection: CollectionAdminView) => collection.name,
|
||||||
(collection) => collection.id,
|
(collection: CollectionAdminView) => collection.id,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (addAccessStatus === 1 && this.showAddAccessToggle) {
|
||||||
|
collectionsToReturn = collectionsToReturn.filter((c: any) => c.addAccess);
|
||||||
|
}
|
||||||
return collectionsToReturn;
|
return collectionsToReturn;
|
||||||
}),
|
}),
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
@ -586,6 +623,57 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the list of collections to see if any collection is orphaned
|
||||||
|
// and will receive the addAccess badge / be filterable by the user
|
||||||
|
async addAccessCollectionsMap(collections: TreeNode<CollectionAdminView>[]) {
|
||||||
|
let mappedCollections;
|
||||||
|
const { type, allowAdminAccessToAllCollectionItems, permissions } = this.organization;
|
||||||
|
|
||||||
|
const canEditCiphersCheck =
|
||||||
|
this._flexibleCollectionsV1FlagEnabled &&
|
||||||
|
!this.organization.canEditAllCiphers(this._flexibleCollectionsV1FlagEnabled);
|
||||||
|
|
||||||
|
// This custom type check will show addAccess badge for
|
||||||
|
// Custom users with canEdit access AND owner/admin manage access setting is OFF
|
||||||
|
const customUserCheck =
|
||||||
|
this._flexibleCollectionsV1FlagEnabled &&
|
||||||
|
!allowAdminAccessToAllCollectionItems &&
|
||||||
|
type === OrganizationUserType.Custom &&
|
||||||
|
permissions.editAnyCollection;
|
||||||
|
|
||||||
|
// If Custom user has Delete Only access they will not see Add Access toggle
|
||||||
|
const customUserOnlyDelete =
|
||||||
|
this.flexibleCollectionsV1Enabled &&
|
||||||
|
type === OrganizationUserType.Custom &&
|
||||||
|
permissions.deleteAnyCollection &&
|
||||||
|
!permissions.editAnyCollection;
|
||||||
|
|
||||||
|
if (!customUserOnlyDelete && (canEditCiphersCheck || customUserCheck)) {
|
||||||
|
mappedCollections = collections.map((c: TreeNode<CollectionAdminView>) => {
|
||||||
|
const groupsCanManage = c.node.groupsCanManage();
|
||||||
|
const usersCanManage = c.node.usersCanManage(this.orgRevokedUsers);
|
||||||
|
if (
|
||||||
|
groupsCanManage.length === 0 &&
|
||||||
|
usersCanManage.length === 0 &&
|
||||||
|
c.node.id !== Unassigned
|
||||||
|
) {
|
||||||
|
c.node.addAccess = true;
|
||||||
|
this.showAddAccessToggle = true;
|
||||||
|
} else {
|
||||||
|
c.node.addAccess = false;
|
||||||
|
}
|
||||||
|
return c.node;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mappedCollections = collections.map((c: TreeNode<CollectionAdminView>) => c.node);
|
||||||
|
}
|
||||||
|
return mappedCollections;
|
||||||
|
}
|
||||||
|
|
||||||
|
addAccessToggle(e: any) {
|
||||||
|
this.addAccessStatus$.next(e);
|
||||||
|
}
|
||||||
|
|
||||||
get loading() {
|
get loading() {
|
||||||
return this.refreshing || this.processingEvent;
|
return this.refreshing || this.processingEvent;
|
||||||
}
|
}
|
||||||
|
@ -2788,6 +2788,12 @@
|
|||||||
"all": {
|
"all": {
|
||||||
"message": "All"
|
"message": "All"
|
||||||
},
|
},
|
||||||
|
"addAccess": {
|
||||||
|
"message": "Add Access"
|
||||||
|
},
|
||||||
|
"addAccessFilter": {
|
||||||
|
"message": "Add Access Filter"
|
||||||
|
},
|
||||||
"refresh": {
|
"refresh": {
|
||||||
"message": "Refresh"
|
"message": "Refresh"
|
||||||
},
|
},
|
||||||
|
@ -61,6 +61,7 @@ describe("Collection", () => {
|
|||||||
const view = await collection.decrypt();
|
const view = await collection.decrypt();
|
||||||
|
|
||||||
expect(view).toEqual({
|
expect(view).toEqual({
|
||||||
|
addAccess: false,
|
||||||
externalId: "extId",
|
externalId: "extId",
|
||||||
hidePasswords: false,
|
hidePasswords: false,
|
||||||
id: "id",
|
id: "id",
|
||||||
|
@ -17,6 +17,7 @@ export class CollectionView implements View, ITreeNodeObject {
|
|||||||
readOnly: boolean = null;
|
readOnly: boolean = null;
|
||||||
hidePasswords: boolean = null;
|
hidePasswords: boolean = null;
|
||||||
manage: boolean = null;
|
manage: boolean = null;
|
||||||
|
addAccess: boolean = false;
|
||||||
assigned: boolean = null;
|
assigned: boolean = null;
|
||||||
|
|
||||||
constructor(c?: Collection | CollectionAccessDetailsResponse) {
|
constructor(c?: Collection | CollectionAccessDetailsResponse) {
|
||||||
|
Loading…
Reference in New Issue
Block a user