mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
Merge branch 'main' into autofill/pm-8027-inline-menu-appears-within-input-fields-that-do-not-relate-to-user-login
This commit is contained in:
62
.github/workflows/deploy-web.yml
vendored
62
.github/workflows/deploy-web.yml
vendored
@@ -112,13 +112,48 @@ jobs:
|
|||||||
echo "azure-login-creds=AZURE_KV_US_DEV_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT
|
echo "azure-login-creds=AZURE_KV_US_DEV_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT
|
||||||
echo "retrieve-secrets-keyvault=webvault-eastus-dev" >> $GITHUB_OUTPUT
|
echo "retrieve-secrets-keyvault=webvault-eastus-dev" >> $GITHUB_OUTPUT
|
||||||
echo "environment-artifact=web-*-cloud-usdev.zip" >> $GITHUB_OUTPUT
|
echo "environment-artifact=web-*-cloud-usdev.zip" >> $GITHUB_OUTPUT
|
||||||
echo "environment-name=Web Vault - US Development Cloud" >> $GITHUB_OUTPUT
|
echo "environment-name=Web Vault - US DEV Cloud" >> $GITHUB_OUTPUT
|
||||||
echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT
|
echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
# Set the sync utility to use for deployment to the environment (az-sync or azcopy)
|
# Set the sync utility to use for deployment to the environment (az-sync or azcopy)
|
||||||
echo "sync-utility=azcopy" >> $GITHUB_OUTPUT
|
echo "sync-utility=azcopy" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Environment Protection
|
||||||
|
env:
|
||||||
|
TAG: ${{ steps.project_tag.outputs.tag }}
|
||||||
|
run: |
|
||||||
|
BRANCH_OR_TAG_LOWER=$(echo ${{ inputs.branch-or-tag }} | awk '{print tolower($0)}')
|
||||||
|
|
||||||
|
PROD_ENV_PATTERN='USPROD|EUPROD'
|
||||||
|
PROD_ALLOWED_TAGS_PATTERN='web-v[0-9]+\.[0-9]+\.[0-9]+'
|
||||||
|
|
||||||
|
QA_ENV_PATTERN='USQA|EUQA'
|
||||||
|
QA_ALLOWED_TAGS_PATTERN='.*'
|
||||||
|
|
||||||
|
DEV_ENV_PATTERN='USDEV'
|
||||||
|
DEV_ALLOWED_TAGS_PATTERN='.*'
|
||||||
|
|
||||||
|
if [[ \
|
||||||
|
${{ inputs.environment }} =~ \.*($PROD_ENV_PATTERN)\.* && \
|
||||||
|
! "$BRANCH_OR_TAG_LOWER" =~ ^($PROD_ALLOWED_TAGS_PATTERN).* \
|
||||||
|
]] || [[ \
|
||||||
|
${{ inputs.environment }} =~ \.*($QA_ENV_PATTERN)\.* && \
|
||||||
|
! "$BRANCH_OR_TAG_LOWER" =~ ^($QA_ALLOWED_TAGS_PATTERN).* \
|
||||||
|
]] || [[ \
|
||||||
|
=~ \.*($DEV_ENV_PATTERN)\.* && \
|
||||||
|
! "$BRANCH_OR_TAG_LOWER" =~ ^($DEV_ALLOWED_TAGS_PATTERN).* \
|
||||||
|
]]; then
|
||||||
|
echo "!Deployment blocked!"
|
||||||
|
echo "Attempting to deploy a tag that is not allowed in ${{ inputs.environment }} environment"
|
||||||
|
echo
|
||||||
|
echo "Environment: ${{ inputs.environment }}
|
||||||
|
echo "Tag: ${{ inputs.branch-or-tag }}
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "${{ inputs.branch-or-tag }} is allowed to deployed on to ${{ inputs.environment }} environment"
|
||||||
|
fi
|
||||||
|
|
||||||
approval:
|
approval:
|
||||||
name: Approval for Deployment to ${{ needs.setup.outputs.environment-name }}
|
name: Approval for Deployment to ${{ needs.setup.outputs.environment-name }}
|
||||||
needs: setup
|
needs: setup
|
||||||
@@ -206,6 +241,31 @@ jobs:
|
|||||||
echo "commit=${{ steps.download-latest-artifacts.outputs.artifact-build-commit }}" >> $GITHUB_OUTPUT
|
echo "commit=${{ steps.download-latest-artifacts.outputs.artifact-build-commit }}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Ensure artifact is from main branch for USDEV environment
|
||||||
|
if: ${{ 'inputs.environment' == 'USDEV'}}
|
||||||
|
run: |
|
||||||
|
# If run-id was used
|
||||||
|
if [ "${{ inputs.build-web-run-id }}" ]; then
|
||||||
|
if [ "${{ steps.download-latest-artifacts.outputs.artifact-build-branch }}" != "main" ]; then
|
||||||
|
echo "Artifact is not from main branch"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If artifact download failed
|
||||||
|
elif [ "${{ steps.download-latest-artifacts.outcome }}" == "failure" ]; then
|
||||||
|
branch=$(gh api /repos/bitwarden/clients/actions/runs/${{ steps.trigger-build-web.outputs.workflow_id }}/artifacts --jq '.artifacts[0].workflow_run.head_branch')
|
||||||
|
if [ "$branch" != "main" ]; then
|
||||||
|
echo "Artifact is not from main branch"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
if [ "${{ steps.download-latest-artifacts.outputs.artifact-build-branch }}" != "main" ]; then
|
||||||
|
echo "Artifact is not from main branch"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
notify-start:
|
notify-start:
|
||||||
name: Notify Slack with start message
|
name: Notify Slack with start message
|
||||||
needs:
|
needs:
|
||||||
|
|||||||
13
.github/workflows/test.yml
vendored
13
.github/workflows/test.yml
vendored
@@ -8,16 +8,27 @@ on:
|
|||||||
- "main"
|
- "main"
|
||||||
- "rc"
|
- "rc"
|
||||||
- "hotfix-rc-*"
|
- "hotfix-rc-*"
|
||||||
pull_request:
|
pull_request_target:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
check-run:
|
||||||
|
name: Check PR run
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
needs: check-run
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
|||||||
@@ -1434,6 +1434,24 @@
|
|||||||
"typeIdentity": {
|
"typeIdentity": {
|
||||||
"message": "Identity"
|
"message": "Identity"
|
||||||
},
|
},
|
||||||
|
"newItemHeader":{
|
||||||
|
"message": "New $TYPE$",
|
||||||
|
"placeholders": {
|
||||||
|
"type": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "Login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editItemHeader":{
|
||||||
|
"message": "Edit $TYPE$",
|
||||||
|
"placeholders": {
|
||||||
|
"type": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "Login"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"passwordHistory": {
|
"passwordHistory": {
|
||||||
"message": "Password history"
|
"message": "Password history"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import { VaultFilterComponent } from "../vault/popup/components/vault/vault-filt
|
|||||||
import { VaultItemsComponent } from "../vault/popup/components/vault/vault-items.component";
|
import { VaultItemsComponent } from "../vault/popup/components/vault/vault-items.component";
|
||||||
import { VaultV2Component } from "../vault/popup/components/vault/vault-v2.component";
|
import { VaultV2Component } from "../vault/popup/components/vault/vault-v2.component";
|
||||||
import { ViewComponent } from "../vault/popup/components/vault/view.component";
|
import { ViewComponent } from "../vault/popup/components/vault/view.component";
|
||||||
|
import { AddEditV2Component } from "../vault/popup/components/vault-v2/add-edit/add-edit-v2.component";
|
||||||
import { AppearanceComponent } from "../vault/popup/settings/appearance.component";
|
import { AppearanceComponent } from "../vault/popup/settings/appearance.component";
|
||||||
import { FolderAddEditComponent } from "../vault/popup/settings/folder-add-edit.component";
|
import { FolderAddEditComponent } from "../vault/popup/settings/folder-add-edit.component";
|
||||||
import { FoldersComponent } from "../vault/popup/settings/folders.component";
|
import { FoldersComponent } from "../vault/popup/settings/folders.component";
|
||||||
@@ -195,20 +196,18 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
data: { state: "cipher-password-history" },
|
data: { state: "cipher-password-history" },
|
||||||
},
|
},
|
||||||
{
|
...extensionRefreshSwap(AddEditComponent, AddEditV2Component, {
|
||||||
path: "add-cipher",
|
path: "add-cipher",
|
||||||
component: AddEditComponent,
|
|
||||||
canActivate: [AuthGuard, debounceNavigationGuard()],
|
canActivate: [AuthGuard, debounceNavigationGuard()],
|
||||||
data: { state: "add-cipher" },
|
data: { state: "add-cipher" },
|
||||||
runGuardsAndResolvers: "always",
|
runGuardsAndResolvers: "always",
|
||||||
},
|
}),
|
||||||
{
|
...extensionRefreshSwap(AddEditComponent, AddEditV2Component, {
|
||||||
path: "edit-cipher",
|
path: "edit-cipher",
|
||||||
component: AddEditComponent,
|
|
||||||
canActivate: [AuthGuard, debounceNavigationGuard()],
|
canActivate: [AuthGuard, debounceNavigationGuard()],
|
||||||
data: { state: "edit-cipher" },
|
data: { state: "edit-cipher" },
|
||||||
runGuardsAndResolvers: "always",
|
runGuardsAndResolvers: "always",
|
||||||
},
|
}),
|
||||||
{
|
{
|
||||||
path: "share-cipher",
|
path: "share-cipher",
|
||||||
component: ShareComponent,
|
component: ShareComponent,
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<popup-page>
|
||||||
|
<popup-header slot="header" [pageTitle]="headerText" showBackButton> </popup-header>
|
||||||
|
|
||||||
|
<popup-footer slot="footer">
|
||||||
|
<button bitButton type="button" buttonType="primary">
|
||||||
|
{{ "save" | i18n }}
|
||||||
|
</button>
|
||||||
|
</popup-footer>
|
||||||
|
</popup-page>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component } from "@angular/core";
|
||||||
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
|
import { FormsModule } from "@angular/forms";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
|
import { SearchModule, ButtonModule } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { PopupFooterComponent } from "../../../../../platform/popup/layout/popup-footer.component";
|
||||||
|
import { PopupHeaderComponent } from "../../../../../platform/popup/layout/popup-header.component";
|
||||||
|
import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-page.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-add-edit-v2",
|
||||||
|
templateUrl: "add-edit-v2.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SearchModule,
|
||||||
|
JslibModule,
|
||||||
|
FormsModule,
|
||||||
|
ButtonModule,
|
||||||
|
PopupPageComponent,
|
||||||
|
PopupHeaderComponent,
|
||||||
|
PopupFooterComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddEditV2Component {
|
||||||
|
headerText: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
) {
|
||||||
|
this.subscribeToParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeToParams(): void {
|
||||||
|
this.route.queryParams.pipe(takeUntilDestroyed()).subscribe((params) => {
|
||||||
|
const isNew = params.isNew.toLowerCase() === "true";
|
||||||
|
const cipherType = parseInt(params.type);
|
||||||
|
|
||||||
|
this.headerText = this.setHeader(isNew, cipherType);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setHeader(isNew: boolean, type: CipherType) {
|
||||||
|
const partOne = isNew ? "newItemHeader" : "editItemHeader";
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case CipherType.Login:
|
||||||
|
return this.i18nService.t(partOne, this.i18nService.t("typeLogin"));
|
||||||
|
case CipherType.Card:
|
||||||
|
return this.i18nService.t(partOne, this.i18nService.t("typeCard"));
|
||||||
|
case CipherType.Identity:
|
||||||
|
return this.i18nService.t(partOne, this.i18nService.t("typeIdentity"));
|
||||||
|
case CipherType.SecureNote:
|
||||||
|
return this.i18nService.t(partOne, this.i18nService.t("note"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<button bitButton [bitMenuTriggerFor]="itemOptions" buttonType="primary" type="button">
|
||||||
|
<i class="bwi bwi-plus-f" aria-hidden="true"></i>
|
||||||
|
{{ "new" | i18n }}
|
||||||
|
</button>
|
||||||
|
<bit-menu #itemOptions>
|
||||||
|
<a type="button" bitMenuItem (click)="newItemNavigate(cipherType.Login)">
|
||||||
|
<i class="bwi bwi-globe" slot="start" aria-hidden="true"></i>
|
||||||
|
{{ "typeLogin" | i18n }}
|
||||||
|
</a>
|
||||||
|
<a type="button" bitMenuItem (click)="newItemNavigate(cipherType.Card)">
|
||||||
|
<i class="bwi bwi-credit-card" slot="start" aria-hidden="true"></i>
|
||||||
|
{{ "typeCard" | i18n }}
|
||||||
|
</a>
|
||||||
|
<a type="button" bitMenuItem (click)="newItemNavigate(cipherType.Identity)">
|
||||||
|
<i class="bwi bwi-id-card" slot="start" aria-hidden="true"></i>
|
||||||
|
{{ "typeIdentity" | i18n }}
|
||||||
|
</a>
|
||||||
|
<a type="button" bitMenuItem (click)="newItemNavigate(cipherType.SecureNote)">
|
||||||
|
<i class="bwi bwi-sticky-note" slot="start" aria-hidden="true"></i>
|
||||||
|
{{ "note" | i18n }}
|
||||||
|
</a>
|
||||||
|
</bit-menu>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
|
import { Router, RouterLink } from "@angular/router";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
|
import { ButtonModule, NoItemsModule, MenuModule } from "@bitwarden/components";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-new-item-dropdown",
|
||||||
|
templateUrl: "new-item-dropdown-v2.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [NoItemsModule, JslibModule, CommonModule, ButtonModule, RouterLink, MenuModule],
|
||||||
|
})
|
||||||
|
export class NewItemDropdownV2Component implements OnInit, OnDestroy {
|
||||||
|
cipherType = CipherType;
|
||||||
|
|
||||||
|
constructor(private router: Router) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {}
|
||||||
|
|
||||||
|
// TODO PM-6826: add selectedVault query param
|
||||||
|
newItemNavigate(type: CipherType) {
|
||||||
|
void this.router.navigate(["/add-cipher"], { queryParams: { type: type, isNew: true } });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
<popup-page>
|
<popup-page>
|
||||||
<popup-header slot="header" [pageTitle]="'vault' | i18n">
|
<popup-header slot="header" [pageTitle]="'vault' | i18n">
|
||||||
<ng-container slot="end">
|
<ng-container slot="end">
|
||||||
<!-- TODO PM-6826: add selectedVault query param -->
|
<app-new-item-dropdown></app-new-item-dropdown>
|
||||||
<a bitButton buttonType="primary" type="button" routerLink="/add-cipher">
|
|
||||||
<i class="bwi bwi-plus-f" aria-hidden="true"></i>
|
|
||||||
{{ "new" | i18n }}
|
|
||||||
</a>
|
|
||||||
<app-pop-out></app-pop-out>
|
<app-pop-out></app-pop-out>
|
||||||
<app-current-account></app-current-account>
|
<app-current-account></app-current-account>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -18,9 +15,7 @@
|
|||||||
<bit-no-items [icon]="vaultIcon">
|
<bit-no-items [icon]="vaultIcon">
|
||||||
<ng-container slot="title">{{ "yourVaultIsEmpty" | i18n }}</ng-container>
|
<ng-container slot="title">{{ "yourVaultIsEmpty" | i18n }}</ng-container>
|
||||||
<ng-container slot="description">{{ "autofillSuggestionsTip" | i18n }}</ng-container>
|
<ng-container slot="description">{{ "autofillSuggestionsTip" | i18n }}</ng-container>
|
||||||
<button slot="button" type="button" bitButton buttonType="primary" (click)="addCipher()">
|
<app-new-item-dropdown slot="button"></app-new-item-dropdown>
|
||||||
{{ "new" | i18n }}
|
|
||||||
</button>
|
|
||||||
</bit-no-items>
|
</bit-no-items>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Router, RouterLink } from "@angular/router";
|
|||||||
import { combineLatest } from "rxjs";
|
import { combineLatest } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { ButtonModule, Icons, NoItemsModule } from "@bitwarden/components";
|
import { ButtonModule, Icons, NoItemsModule } from "@bitwarden/components";
|
||||||
|
|
||||||
import { CurrentAccountComponent } from "../../../../auth/popup/account-switching/current-account.component";
|
import { CurrentAccountComponent } from "../../../../auth/popup/account-switching/current-account.component";
|
||||||
@@ -13,6 +14,7 @@ import { PopupHeaderComponent } from "../../../../platform/popup/layout/popup-he
|
|||||||
import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page.component";
|
import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page.component";
|
||||||
import { VaultPopupItemsService } from "../../services/vault-popup-items.service";
|
import { VaultPopupItemsService } from "../../services/vault-popup-items.service";
|
||||||
import { AutofillVaultListItemsComponent, VaultListItemsContainerComponent } from "../vault-v2";
|
import { AutofillVaultListItemsComponent, VaultListItemsContainerComponent } from "../vault-v2";
|
||||||
|
import { NewItemDropdownV2Component } from "../vault-v2/new-item-dropdown/new-item-dropdown-v2.component";
|
||||||
import { VaultListFiltersComponent } from "../vault-v2/vault-list-filters/vault-list-filters.component";
|
import { VaultListFiltersComponent } from "../vault-v2/vault-list-filters/vault-list-filters.component";
|
||||||
import { VaultV2SearchComponent } from "../vault-v2/vault-search/vault-v2-search.component";
|
import { VaultV2SearchComponent } from "../vault-v2/vault-search/vault-v2-search.component";
|
||||||
|
|
||||||
@@ -40,9 +42,11 @@ enum VaultState {
|
|||||||
ButtonModule,
|
ButtonModule,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
VaultV2SearchComponent,
|
VaultV2SearchComponent,
|
||||||
|
NewItemDropdownV2Component,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class VaultV2Component implements OnInit, OnDestroy {
|
export class VaultV2Component implements OnInit, OnDestroy {
|
||||||
|
cipherType = CipherType;
|
||||||
protected favoriteCiphers$ = this.vaultPopupItemsService.favoriteCiphers$;
|
protected favoriteCiphers$ = this.vaultPopupItemsService.favoriteCiphers$;
|
||||||
protected remainingCiphers$ = this.vaultPopupItemsService.remainingCiphers$;
|
protected remainingCiphers$ = this.vaultPopupItemsService.remainingCiphers$;
|
||||||
|
|
||||||
@@ -86,9 +90,4 @@ export class VaultV2Component implements OnInit, OnDestroy {
|
|||||||
ngOnInit(): void {}
|
ngOnInit(): void {}
|
||||||
|
|
||||||
ngOnDestroy(): void {}
|
ngOnDestroy(): void {}
|
||||||
|
|
||||||
addCipher() {
|
|
||||||
// TODO: Add currently filtered organization to query params if available
|
|
||||||
void this.router.navigate(["/add-cipher"], {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,7 +198,9 @@ export class ViewComponent extends BaseViewComponent {
|
|||||||
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.router.navigate(["/edit-cipher"], { queryParams: { cipherId: this.cipher.id } });
|
this.router.navigate(["/edit-cipher"], {
|
||||||
|
queryParams: { cipherId: this.cipher.id, type: this.cipher.type, isNew: false },
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -379,6 +379,54 @@ describe("VaultPopupItemsService", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("loading$", () => {
|
||||||
|
let tracked: ObservableTracker<boolean>;
|
||||||
|
let trackedCiphers: ObservableTracker<any>;
|
||||||
|
beforeEach(() => {
|
||||||
|
// Start tracking loading$ emissions
|
||||||
|
tracked = new ObservableTracker(service.loading$);
|
||||||
|
|
||||||
|
// Track remainingCiphers$ to make cipher observables active
|
||||||
|
trackedCiphers = new ObservableTracker(service.remainingCiphers$);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should initialize with true first", async () => {
|
||||||
|
expect(tracked.emissions[0]).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should emit false once ciphers are available", async () => {
|
||||||
|
expect(tracked.emissions.length).toBe(2);
|
||||||
|
expect(tracked.emissions[0]).toBe(true);
|
||||||
|
expect(tracked.emissions[1]).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cycle when cipherService.ciphers$ emits", async () => {
|
||||||
|
// Restart tracking
|
||||||
|
tracked = new ObservableTracker(service.loading$);
|
||||||
|
(cipherServiceMock.ciphers$ as BehaviorSubject<any>).next(null);
|
||||||
|
|
||||||
|
await trackedCiphers.pauseUntilReceived(2);
|
||||||
|
|
||||||
|
expect(tracked.emissions.length).toBe(3);
|
||||||
|
expect(tracked.emissions[0]).toBe(false);
|
||||||
|
expect(tracked.emissions[1]).toBe(true);
|
||||||
|
expect(tracked.emissions[2]).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cycle when filters are applied", async () => {
|
||||||
|
// Restart tracking
|
||||||
|
tracked = new ObservableTracker(service.loading$);
|
||||||
|
service.applyFilter("test");
|
||||||
|
|
||||||
|
await trackedCiphers.pauseUntilReceived(2);
|
||||||
|
|
||||||
|
expect(tracked.emissions.length).toBe(3);
|
||||||
|
expect(tracked.emissions[0]).toBe(false);
|
||||||
|
expect(tracked.emissions[1]).toBe(true);
|
||||||
|
expect(tracked.emissions[2]).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("applyFilter", () => {
|
describe("applyFilter", () => {
|
||||||
it("should call search Service with the new search term", (done) => {
|
it("should call search Service with the new search term", (done) => {
|
||||||
const searchText = "Hello";
|
const searchText = "Hello";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { inject, Injectable, NgZone } from "@angular/core";
|
|||||||
import {
|
import {
|
||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
combineLatest,
|
combineLatest,
|
||||||
|
distinctUntilChanged,
|
||||||
distinctUntilKeyChanged,
|
distinctUntilKeyChanged,
|
||||||
from,
|
from,
|
||||||
map,
|
map,
|
||||||
@@ -12,6 +13,8 @@ import {
|
|||||||
startWith,
|
startWith,
|
||||||
Subject,
|
Subject,
|
||||||
switchMap,
|
switchMap,
|
||||||
|
tap,
|
||||||
|
withLatestFrom,
|
||||||
} from "rxjs";
|
} from "rxjs";
|
||||||
|
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||||
@@ -40,6 +43,13 @@ import { MY_VAULT_ID, VaultPopupListFiltersService } from "./vault-popup-list-fi
|
|||||||
export class VaultPopupItemsService {
|
export class VaultPopupItemsService {
|
||||||
private _refreshCurrentTab$ = new Subject<void>();
|
private _refreshCurrentTab$ = new Subject<void>();
|
||||||
private _searchText$ = new BehaviorSubject<string>("");
|
private _searchText$ = new BehaviorSubject<string>("");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subject that emits whenever new ciphers are being processed/filtered.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _ciphersLoading$ = new Subject<void>();
|
||||||
|
|
||||||
latestSearchText$: Observable<string> = this._searchText$.asObservable();
|
latestSearchText$: Observable<string> = this._searchText$.asObservable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,6 +94,7 @@ export class VaultPopupItemsService {
|
|||||||
this.cipherService.localData$,
|
this.cipherService.localData$,
|
||||||
).pipe(
|
).pipe(
|
||||||
runInsideAngular(inject(NgZone)), // Workaround to ensure cipher$ state provider emissions are run inside Angular
|
runInsideAngular(inject(NgZone)), // Workaround to ensure cipher$ state provider emissions are run inside Angular
|
||||||
|
tap(() => this._ciphersLoading$.next()),
|
||||||
switchMap(() => Utils.asyncToObservable(() => this.cipherService.getAllDecrypted())),
|
switchMap(() => Utils.asyncToObservable(() => this.cipherService.getAllDecrypted())),
|
||||||
switchMap((ciphers) =>
|
switchMap((ciphers) =>
|
||||||
combineLatest([
|
combineLatest([
|
||||||
@@ -112,6 +123,7 @@ export class VaultPopupItemsService {
|
|||||||
this._searchText$,
|
this._searchText$,
|
||||||
this.vaultPopupListFiltersService.filterFunction$,
|
this.vaultPopupListFiltersService.filterFunction$,
|
||||||
]).pipe(
|
]).pipe(
|
||||||
|
tap(() => this._ciphersLoading$.next()),
|
||||||
map(([ciphers, searchText, filterFunction]): [CipherView[], string] => [
|
map(([ciphers, searchText, filterFunction]): [CipherView[], string] => [
|
||||||
filterFunction(ciphers),
|
filterFunction(ciphers),
|
||||||
searchText,
|
searchText,
|
||||||
@@ -148,10 +160,8 @@ export class VaultPopupItemsService {
|
|||||||
* List of favorite ciphers that are not currently suggested for autofill.
|
* List of favorite ciphers that are not currently suggested for autofill.
|
||||||
* Ciphers are sorted by last used date, then by name.
|
* Ciphers are sorted by last used date, then by name.
|
||||||
*/
|
*/
|
||||||
favoriteCiphers$: Observable<PopupCipherView[]> = combineLatest([
|
favoriteCiphers$: Observable<PopupCipherView[]> = this.autoFillCiphers$.pipe(
|
||||||
this.autoFillCiphers$,
|
withLatestFrom(this._filteredCipherList$),
|
||||||
this._filteredCipherList$,
|
|
||||||
]).pipe(
|
|
||||||
map(([autoFillCiphers, ciphers]) =>
|
map(([autoFillCiphers, ciphers]) =>
|
||||||
ciphers.filter((cipher) => cipher.favorite && !autoFillCiphers.includes(cipher)),
|
ciphers.filter((cipher) => cipher.favorite && !autoFillCiphers.includes(cipher)),
|
||||||
),
|
),
|
||||||
@@ -165,12 +175,9 @@ export class VaultPopupItemsService {
|
|||||||
* List of all remaining ciphers that are not currently suggested for autofill or marked as favorite.
|
* List of all remaining ciphers that are not currently suggested for autofill or marked as favorite.
|
||||||
* Ciphers are sorted by name.
|
* Ciphers are sorted by name.
|
||||||
*/
|
*/
|
||||||
remainingCiphers$: Observable<PopupCipherView[]> = combineLatest([
|
remainingCiphers$: Observable<PopupCipherView[]> = this.favoriteCiphers$.pipe(
|
||||||
this.autoFillCiphers$,
|
withLatestFrom(this._filteredCipherList$, this.autoFillCiphers$),
|
||||||
this.favoriteCiphers$,
|
map(([favoriteCiphers, ciphers, autoFillCiphers]) =>
|
||||||
this._filteredCipherList$,
|
|
||||||
]).pipe(
|
|
||||||
map(([autoFillCiphers, favoriteCiphers, ciphers]) =>
|
|
||||||
ciphers.filter(
|
ciphers.filter(
|
||||||
(cipher) => !autoFillCiphers.includes(cipher) && !favoriteCiphers.includes(cipher),
|
(cipher) => !autoFillCiphers.includes(cipher) && !favoriteCiphers.includes(cipher),
|
||||||
),
|
),
|
||||||
@@ -179,6 +186,14 @@ export class VaultPopupItemsService {
|
|||||||
shareReplay({ refCount: false, bufferSize: 1 }),
|
shareReplay({ refCount: false, bufferSize: 1 }),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable that indicates whether the service is currently loading ciphers.
|
||||||
|
*/
|
||||||
|
loading$: Observable<boolean> = merge(
|
||||||
|
this._ciphersLoading$.pipe(map(() => true)),
|
||||||
|
this.remainingCiphers$.pipe(map(() => false)),
|
||||||
|
).pipe(startWith(true), distinctUntilChanged(), shareReplay({ refCount: false, bufferSize: 1 }));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Observable that indicates whether a filter is currently applied to the ciphers.
|
* Observable that indicates whether a filter is currently applied to the ciphers.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { FormBuilder } from "@angular/forms";
|
|||||||
import { BehaviorSubject, skipWhile } from "rxjs";
|
import { BehaviorSubject, skipWhile } from "rxjs";
|
||||||
|
|
||||||
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 { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
|
import { PolicyType } 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 { ProductType } from "@bitwarden/common/enums";
|
import { ProductType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@@ -23,6 +25,7 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
const folderViews$ = new BehaviorSubject([]);
|
const folderViews$ = new BehaviorSubject([]);
|
||||||
const cipherViews$ = new BehaviorSubject({});
|
const cipherViews$ = new BehaviorSubject({});
|
||||||
const decryptedCollections$ = new BehaviorSubject<CollectionView[]>([]);
|
const decryptedCollections$ = new BehaviorSubject<CollectionView[]>([]);
|
||||||
|
const policyAppliesToActiveUser$ = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
const collectionService = {
|
const collectionService = {
|
||||||
decryptedCollections$,
|
decryptedCollections$,
|
||||||
@@ -45,9 +48,15 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
t: (key: string) => key,
|
t: (key: string) => key,
|
||||||
} as I18nService;
|
} as I18nService;
|
||||||
|
|
||||||
|
const policyService = {
|
||||||
|
policyAppliesToActiveUser$: jest.fn(() => policyAppliesToActiveUser$),
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
memberOrganizations$.next([]);
|
memberOrganizations$.next([]);
|
||||||
decryptedCollections$.next([]);
|
decryptedCollections$.next([]);
|
||||||
|
policyAppliesToActiveUser$.next(false);
|
||||||
|
policyService.policyAppliesToActiveUser$.mockClear();
|
||||||
|
|
||||||
collectionService.getAllNested = () => Promise.resolve([]);
|
collectionService.getAllNested = () => Promise.resolve([]);
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
@@ -72,6 +81,10 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
provide: CollectionService,
|
provide: CollectionService,
|
||||||
useValue: collectionService,
|
useValue: collectionService,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: PolicyService,
|
||||||
|
useValue: policyService,
|
||||||
|
},
|
||||||
{ provide: FormBuilder, useClass: FormBuilder },
|
{ provide: FormBuilder, useClass: FormBuilder },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -127,6 +140,65 @@ describe("VaultPopupListFiltersService", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("PersonalOwnership policy", () => {
|
||||||
|
it('calls policyAppliesToActiveUser$ with "PersonalOwnership"', () => {
|
||||||
|
expect(policyService.policyAppliesToActiveUser$).toHaveBeenCalledWith(
|
||||||
|
PolicyType.PersonalOwnership,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an empty array when the policy applies and there is a single organization", (done) => {
|
||||||
|
policyAppliesToActiveUser$.next(true);
|
||||||
|
memberOrganizations$.next([
|
||||||
|
{ name: "bobby's org", id: "1234-3323-23223" },
|
||||||
|
] as Organization[]);
|
||||||
|
|
||||||
|
service.organizations$.subscribe((organizations) => {
|
||||||
|
expect(organizations).toEqual([]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds "myVault" when the policy does not apply and there are multiple organizations', (done) => {
|
||||||
|
policyAppliesToActiveUser$.next(false);
|
||||||
|
const orgs = [
|
||||||
|
{ name: "bobby's org", id: "1234-3323-23223" },
|
||||||
|
{ name: "alice's org", id: "2223-4343-99888" },
|
||||||
|
] as Organization[];
|
||||||
|
|
||||||
|
memberOrganizations$.next(orgs);
|
||||||
|
|
||||||
|
service.organizations$.subscribe((organizations) => {
|
||||||
|
expect(organizations.map((o) => o.label)).toEqual([
|
||||||
|
"myVault",
|
||||||
|
"alice's org",
|
||||||
|
"bobby's org",
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not add "myVault" the policy applies and there are multiple organizations', (done) => {
|
||||||
|
policyAppliesToActiveUser$.next(true);
|
||||||
|
const orgs = [
|
||||||
|
{ name: "bobby's org", id: "1234-3323-23223" },
|
||||||
|
{ name: "alice's org", id: "2223-3242-99888" },
|
||||||
|
{ name: "catherine's org", id: "77733-4343-99888" },
|
||||||
|
] as Organization[];
|
||||||
|
|
||||||
|
memberOrganizations$.next(orgs);
|
||||||
|
|
||||||
|
service.organizations$.subscribe((organizations) => {
|
||||||
|
expect(organizations.map((o) => o.label)).toEqual([
|
||||||
|
"alice's org",
|
||||||
|
"bobby's org",
|
||||||
|
"catherine's org",
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("icons", () => {
|
describe("icons", () => {
|
||||||
it("sets family icon for family organizations", (done) => {
|
it("sets family icon for family organizations", (done) => {
|
||||||
const orgs = [
|
const orgs = [
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import {
|
|||||||
|
|
||||||
import { DynamicTreeNode } from "@bitwarden/angular/vault/vault-filter/models/dynamic-tree-node.model";
|
import { DynamicTreeNode } from "@bitwarden/angular/vault/vault-filter/models/dynamic-tree-node.model";
|
||||||
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 { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
|
import { PolicyType } 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 { ProductType } from "@bitwarden/common/enums";
|
import { ProductType } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@@ -88,6 +90,7 @@ export class VaultPopupListFiltersService {
|
|||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private collectionService: CollectionService,
|
private collectionService: CollectionService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
|
private policyService: PolicyService,
|
||||||
) {
|
) {
|
||||||
this.filterForm.controls.organization.valueChanges
|
this.filterForm.controls.organization.valueChanges
|
||||||
.pipe(takeUntilDestroyed())
|
.pipe(takeUntilDestroyed())
|
||||||
@@ -167,44 +170,63 @@ export class VaultPopupListFiltersService {
|
|||||||
/**
|
/**
|
||||||
* Organization array structured to be directly passed to `ChipSelectComponent`
|
* Organization array structured to be directly passed to `ChipSelectComponent`
|
||||||
*/
|
*/
|
||||||
organizations$: Observable<ChipSelectOption<Organization>[]> =
|
organizations$: Observable<ChipSelectOption<Organization>[]> = combineLatest([
|
||||||
this.organizationService.memberOrganizations$.pipe(
|
this.organizationService.memberOrganizations$,
|
||||||
map((orgs) => orgs.sort(Utils.getSortFunction(this.i18nService, "name"))),
|
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
|
||||||
map((orgs) => {
|
]).pipe(
|
||||||
if (!orgs.length) {
|
map(([orgs, personalOwnershipApplies]): [Organization[], boolean] => [
|
||||||
return [];
|
orgs.sort(Utils.getSortFunction(this.i18nService, "name")),
|
||||||
}
|
personalOwnershipApplies,
|
||||||
|
]),
|
||||||
|
map(([orgs, personalOwnershipApplies]) => {
|
||||||
|
// When there are no organizations return an empty array,
|
||||||
|
// resulting in the org filter being hidden
|
||||||
|
if (!orgs.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
// When there is only one organization and personal ownership policy applies,
|
||||||
// When the user is a member of an organization, make the "My Vault" option available
|
// return an empty array, resulting in the org filter being hidden
|
||||||
{
|
if (orgs.length === 1 && personalOwnershipApplies) {
|
||||||
value: { id: MY_VAULT_ID } as Organization,
|
return [];
|
||||||
label: this.i18nService.t("myVault"),
|
}
|
||||||
icon: "bwi-user",
|
|
||||||
},
|
|
||||||
...orgs.map((org) => {
|
|
||||||
let icon = "bwi-business";
|
|
||||||
|
|
||||||
if (!org.enabled) {
|
const myVaultOrg: ChipSelectOption<Organization>[] = [];
|
||||||
// Show a warning icon if the organization is deactivated
|
|
||||||
icon = "bwi-exclamation-triangle tw-text-danger";
|
|
||||||
} else if (
|
|
||||||
org.planProductType === ProductType.Families ||
|
|
||||||
org.planProductType === ProductType.Free
|
|
||||||
) {
|
|
||||||
// Show a family icon if the organization is a family or free org
|
|
||||||
icon = "bwi-family";
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
// Only add "My vault" if personal ownership policy does not apply
|
||||||
value: org,
|
if (!personalOwnershipApplies) {
|
||||||
label: org.name,
|
myVaultOrg.push({
|
||||||
icon,
|
value: { id: MY_VAULT_ID } as Organization,
|
||||||
};
|
label: this.i18nService.t("myVault"),
|
||||||
}),
|
icon: "bwi-user",
|
||||||
];
|
});
|
||||||
}),
|
}
|
||||||
);
|
|
||||||
|
return [
|
||||||
|
...myVaultOrg,
|
||||||
|
...orgs.map((org) => {
|
||||||
|
let icon = "bwi-business";
|
||||||
|
|
||||||
|
if (!org.enabled) {
|
||||||
|
// Show a warning icon if the organization is deactivated
|
||||||
|
icon = "bwi-exclamation-triangle tw-text-danger";
|
||||||
|
} else if (
|
||||||
|
org.planProductType === ProductType.Families ||
|
||||||
|
org.planProductType === ProductType.Free
|
||||||
|
) {
|
||||||
|
// Show a family icon if the organization is a family or free org
|
||||||
|
icon = "bwi-family";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: org,
|
||||||
|
label: org.name,
|
||||||
|
icon,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Folder array structured to be directly passed to `ChipSelectComponent`
|
* Folder array structured to be directly passed to `ChipSelectComponent`
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
"papaparse": "5.4.1",
|
"papaparse": "5.4.1",
|
||||||
"proper-lockfile": "4.1.2",
|
"proper-lockfile": "4.1.2",
|
||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.1",
|
||||||
"tldts": "6.1.22",
|
"tldts": "6.1.25",
|
||||||
"zxcvbn": "4.4.2"
|
"zxcvbn": "4.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ export class OrganizationCollectionRequest extends CollectionExport {
|
|||||||
req.name = "Collection name";
|
req.name = "Collection name";
|
||||||
req.externalId = null;
|
req.externalId = null;
|
||||||
req.groups = [SelectionReadOnly.template(), SelectionReadOnly.template()];
|
req.groups = [SelectionReadOnly.template(), SelectionReadOnly.template()];
|
||||||
|
req.users = [SelectionReadOnly.template(), SelectionReadOnly.template()];
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
groups: SelectionReadOnly[];
|
groups: SelectionReadOnly[];
|
||||||
|
users: SelectionReadOnly[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,10 +170,17 @@ export class EditCommand {
|
|||||||
: req.groups.map(
|
: req.groups.map(
|
||||||
(g) => new SelectionReadOnlyRequest(g.id, g.readOnly, g.hidePasswords, g.manage),
|
(g) => new SelectionReadOnlyRequest(g.id, g.readOnly, g.hidePasswords, g.manage),
|
||||||
);
|
);
|
||||||
|
const users =
|
||||||
|
req.users == null
|
||||||
|
? null
|
||||||
|
: req.users.map(
|
||||||
|
(u) => new SelectionReadOnlyRequest(u.id, u.readOnly, u.hidePasswords, u.manage),
|
||||||
|
);
|
||||||
const request = new CollectionRequest();
|
const request = new CollectionRequest();
|
||||||
request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString;
|
request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString;
|
||||||
request.externalId = req.externalId;
|
request.externalId = req.externalId;
|
||||||
request.groups = groups;
|
request.groups = groups;
|
||||||
|
request.users = users;
|
||||||
const response = await this.apiService.putCollection(req.organizationId, id, request);
|
const response = await this.apiService.putCollection(req.organizationId, id, request);
|
||||||
const view = CollectionExport.toView(req);
|
const view = CollectionExport.toView(req);
|
||||||
view.id = response.id;
|
view.id = response.id;
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export class ServeCommand {
|
|||||||
this.serviceContainer.apiService,
|
this.serviceContainer.apiService,
|
||||||
this.serviceContainer.folderApiService,
|
this.serviceContainer.folderApiService,
|
||||||
this.serviceContainer.billingAccountProfileStateService,
|
this.serviceContainer.billingAccountProfileStateService,
|
||||||
|
this.serviceContainer.organizationService,
|
||||||
);
|
);
|
||||||
this.editCommand = new EditCommand(
|
this.editCommand = new EditCommand(
|
||||||
this.serviceContainer.cipherService,
|
this.serviceContainer.cipherService,
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ export class VaultProgram extends BaseProgram {
|
|||||||
this.serviceContainer.apiService,
|
this.serviceContainer.apiService,
|
||||||
this.serviceContainer.folderApiService,
|
this.serviceContainer.folderApiService,
|
||||||
this.serviceContainer.billingAccountProfileStateService,
|
this.serviceContainer.billingAccountProfileStateService,
|
||||||
|
this.serviceContainer.organizationService,
|
||||||
);
|
);
|
||||||
const response = await command.run(object, encodedJson, cmd);
|
const response = await command.run(object, encodedJson, cmd);
|
||||||
this.processResponse(response);
|
this.processResponse(response);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import * as path from "path";
|
|||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
|
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
|
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
|
||||||
@@ -32,6 +33,7 @@ export class CreateCommand {
|
|||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private folderApiService: FolderApiServiceAbstraction,
|
private folderApiService: FolderApiServiceAbstraction,
|
||||||
private accountProfileService: BillingAccountProfileStateService,
|
private accountProfileService: BillingAccountProfileStateService,
|
||||||
|
private organizationService: OrganizationService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async run(
|
async run(
|
||||||
@@ -183,6 +185,8 @@ export class CreateCommand {
|
|||||||
if (orgKey == null) {
|
if (orgKey == null) {
|
||||||
throw new Error("No encryption key for this organization.");
|
throw new Error("No encryption key for this organization.");
|
||||||
}
|
}
|
||||||
|
const organization = await this.organizationService.get(req.organizationId);
|
||||||
|
const currentOrgUserId = organization.organizationUserId;
|
||||||
|
|
||||||
const groups =
|
const groups =
|
||||||
req.groups == null
|
req.groups == null
|
||||||
@@ -190,10 +194,17 @@ export class CreateCommand {
|
|||||||
: req.groups.map(
|
: req.groups.map(
|
||||||
(g) => new SelectionReadOnlyRequest(g.id, g.readOnly, g.hidePasswords, g.manage),
|
(g) => new SelectionReadOnlyRequest(g.id, g.readOnly, g.hidePasswords, g.manage),
|
||||||
);
|
);
|
||||||
|
const users =
|
||||||
|
req.users == null
|
||||||
|
? [new SelectionReadOnlyRequest(currentOrgUserId, false, false, true)]
|
||||||
|
: req.users.map(
|
||||||
|
(u) => new SelectionReadOnlyRequest(u.id, u.readOnly, u.hidePasswords, u.manage),
|
||||||
|
);
|
||||||
const request = new CollectionRequest();
|
const request = new CollectionRequest();
|
||||||
request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString;
|
request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString;
|
||||||
request.externalId = req.externalId;
|
request.externalId = req.externalId;
|
||||||
request.groups = groups;
|
request.groups = groups;
|
||||||
|
request.users = users;
|
||||||
const response = await this.apiService.postCollection(req.organizationId, request);
|
const response = await this.apiService.postCollection(req.organizationId, request);
|
||||||
const view = CollectionExport.toView(req);
|
const view = CollectionExport.toView(req);
|
||||||
view.id = response.id;
|
view.id = response.id;
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ export class InternalGroupService extends GroupService {
|
|||||||
async save(group: GroupView): Promise<GroupView> {
|
async save(group: GroupView): Promise<GroupView> {
|
||||||
const request = new GroupRequest();
|
const request = new GroupRequest();
|
||||||
request.name = group.name;
|
request.name = group.name;
|
||||||
request.accessAll = group.accessAll;
|
|
||||||
request.users = group.members;
|
request.users = group.members;
|
||||||
request.collections = group.collections.map(
|
request.collections = group.collections.map(
|
||||||
(c) => new SelectionReadOnlyRequest(c.id, c.readOnly, c.hidePasswords, c.manage),
|
(c) => new SelectionReadOnlyRequest(c.id, c.readOnly, c.hidePasswords, c.manage),
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models
|
|||||||
|
|
||||||
export class GroupRequest {
|
export class GroupRequest {
|
||||||
name: string;
|
name: string;
|
||||||
accessAll: boolean;
|
|
||||||
collections: SelectionReadOnlyRequest[] = [];
|
collections: SelectionReadOnlyRequest[] = [];
|
||||||
users: string[] = [];
|
users: string[] = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,6 @@ export class GroupResponse extends BaseResponse {
|
|||||||
id: string;
|
id: string;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
name: string;
|
name: string;
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* To be removed after Flexible Collections.
|
|
||||||
**/
|
|
||||||
accessAll: boolean;
|
|
||||||
externalId: string;
|
externalId: string;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
@@ -17,7 +12,6 @@ export class GroupResponse extends BaseResponse {
|
|||||||
this.id = this.getResponseProperty("Id");
|
this.id = this.getResponseProperty("Id");
|
||||||
this.organizationId = this.getResponseProperty("OrganizationId");
|
this.organizationId = this.getResponseProperty("OrganizationId");
|
||||||
this.name = this.getResponseProperty("Name");
|
this.name = this.getResponseProperty("Name");
|
||||||
this.accessAll = this.getResponseProperty("AccessAll");
|
|
||||||
this.externalId = this.getResponseProperty("ExternalId");
|
this.externalId = this.getResponseProperty("ExternalId");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ export class UserAdminService {
|
|||||||
|
|
||||||
async save(user: OrganizationUserAdminView): Promise<void> {
|
async save(user: OrganizationUserAdminView): Promise<void> {
|
||||||
const request = new OrganizationUserUpdateRequest();
|
const request = new OrganizationUserUpdateRequest();
|
||||||
request.accessAll = user.accessAll;
|
|
||||||
request.permissions = user.permissions;
|
request.permissions = user.permissions;
|
||||||
request.type = user.type;
|
request.type = user.type;
|
||||||
request.collections = user.collections;
|
request.collections = user.collections;
|
||||||
@@ -54,7 +53,6 @@ export class UserAdminService {
|
|||||||
async invite(emails: string[], user: OrganizationUserAdminView): Promise<void> {
|
async invite(emails: string[], user: OrganizationUserAdminView): Promise<void> {
|
||||||
const request = new OrganizationUserInviteRequest();
|
const request = new OrganizationUserInviteRequest();
|
||||||
request.emails = emails;
|
request.emails = emails;
|
||||||
request.accessAll = user.accessAll;
|
|
||||||
request.permissions = user.permissions;
|
request.permissions = user.permissions;
|
||||||
request.type = user.type;
|
request.type = user.type;
|
||||||
request.collections = user.collections;
|
request.collections = user.collections;
|
||||||
@@ -77,7 +75,6 @@ export class UserAdminService {
|
|||||||
view.type = u.type;
|
view.type = u.type;
|
||||||
view.status = u.status;
|
view.status = u.status;
|
||||||
view.externalId = u.externalId;
|
view.externalId = u.externalId;
|
||||||
view.accessAll = u.accessAll;
|
|
||||||
view.permissions = u.permissions;
|
view.permissions = u.permissions;
|
||||||
view.resetPasswordEnrolled = u.resetPasswordEnrolled;
|
view.resetPasswordEnrolled = u.resetPasswordEnrolled;
|
||||||
view.collections = u.collections.map((c) => ({
|
view.collections = u.collections.map((c) => ({
|
||||||
|
|||||||
@@ -8,12 +8,6 @@ export class GroupView implements View {
|
|||||||
id: string;
|
id: string;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
name: string;
|
name: string;
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* To be removed after Flexible Collections.
|
|
||||||
* This will always return `false` if Flexible Collections is enabled.
|
|
||||||
**/
|
|
||||||
accessAll: boolean;
|
|
||||||
externalId: string;
|
externalId: string;
|
||||||
collections: CollectionAccessSelectionView[] = [];
|
collections: CollectionAccessSelectionView[] = [];
|
||||||
members: string[] = [];
|
members: string[] = [];
|
||||||
|
|||||||
@@ -13,12 +13,6 @@ export class OrganizationUserAdminView {
|
|||||||
type: OrganizationUserType;
|
type: OrganizationUserType;
|
||||||
status: OrganizationUserStatusType;
|
status: OrganizationUserStatusType;
|
||||||
externalId: string;
|
externalId: string;
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* To be removed after Flexible Collections.
|
|
||||||
* This will always return `false` if Flexible Collections is enabled.
|
|
||||||
**/
|
|
||||||
accessAll: boolean;
|
|
||||||
permissions: PermissionsApi;
|
permissions: PermissionsApi;
|
||||||
resetPasswordEnrolled: boolean;
|
resetPasswordEnrolled: boolean;
|
||||||
hasMasterPassword: boolean;
|
hasMasterPassword: boolean;
|
||||||
|
|||||||
@@ -12,12 +12,6 @@ export class OrganizationUserView {
|
|||||||
userId: string;
|
userId: string;
|
||||||
type: OrganizationUserType;
|
type: OrganizationUserType;
|
||||||
status: OrganizationUserStatusType;
|
status: OrganizationUserStatusType;
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* To be removed after Flexible Collections.
|
|
||||||
* This will always return `false` if Flexible Collections is enabled.
|
|
||||||
**/
|
|
||||||
accessAll: boolean;
|
|
||||||
permissions: PermissionsApi;
|
permissions: PermissionsApi;
|
||||||
resetPasswordEnrolled: boolean;
|
resetPasswordEnrolled: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<bit-nav-item
|
<bit-nav-item
|
||||||
icon="bwi-collection"
|
icon="bwi-collection"
|
||||||
[text]="(organization.flexibleCollections ? 'collections' : 'vault') | i18n"
|
[text]="'collections' | i18n"
|
||||||
route="vault"
|
route="vault"
|
||||||
*ngIf="canShowVaultTab(organization)"
|
*ngIf="canShowVaultTab(organization)"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -45,7 +45,6 @@
|
|||||||
[columnHeader]="'member' | i18n"
|
[columnHeader]="'member' | i18n"
|
||||||
[selectorLabelText]="'selectMembers' | i18n"
|
[selectorLabelText]="'selectMembers' | i18n"
|
||||||
[emptySelectionText]="'noMembersAdded' | i18n"
|
[emptySelectionText]="'noMembersAdded' | i18n"
|
||||||
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async"
|
|
||||||
></bit-access-selector>
|
></bit-access-selector>
|
||||||
</bit-tab>
|
</bit-tab>
|
||||||
|
|
||||||
@@ -56,24 +55,14 @@
|
|||||||
{{ "restrictedCollectionAssignmentDesc" | i18n }}
|
{{ "restrictedCollectionAssignmentDesc" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<div *ngIf="!(flexibleCollectionsEnabled$ | async)" class="tw-my-3">
|
<bit-access-selector
|
||||||
<input type="checkbox" formControlName="accessAll" id="accessAll" />
|
formControlName="collections"
|
||||||
<label class="tw-mb-0 tw-text-lg" for="accessAll">{{
|
[items]="collections"
|
||||||
"accessAllCollectionsDesc" | i18n
|
[permissionMode]="PermissionMode.Edit"
|
||||||
}}</label>
|
[columnHeader]="'collection' | i18n"
|
||||||
<p class="tw-my-0 tw-text-muted">{{ "accessAllCollectionsHelp" | i18n }}</p>
|
[selectorLabelText]="'selectCollections' | i18n"
|
||||||
</div>
|
[emptySelectionText]="'noCollectionsAdded' | i18n"
|
||||||
<ng-container *ngIf="!groupForm.value.accessAll">
|
></bit-access-selector>
|
||||||
<bit-access-selector
|
|
||||||
formControlName="collections"
|
|
||||||
[items]="collections"
|
|
||||||
[permissionMode]="PermissionMode.Edit"
|
|
||||||
[columnHeader]="'collection' | i18n"
|
|
||||||
[selectorLabelText]="'selectCollections' | i18n"
|
|
||||||
[emptySelectionText]="'noCollectionsAdded' | i18n"
|
|
||||||
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async"
|
|
||||||
></bit-access-selector>
|
|
||||||
</ng-container>
|
|
||||||
</bit-tab>
|
</bit-tab>
|
||||||
</bit-tab-group>
|
</bit-tab-group>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -96,9 +96,6 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
private organization$ = this.organizationService
|
private organization$ = this.organizationService
|
||||||
.get$(this.organizationId)
|
.get$(this.organizationId)
|
||||||
.pipe(shareReplay({ refCount: true }));
|
.pipe(shareReplay({ refCount: true }));
|
||||||
protected flexibleCollectionsEnabled$ = this.organization$.pipe(
|
|
||||||
map((o) => o?.flexibleCollections),
|
|
||||||
);
|
|
||||||
private flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
|
private flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
|
||||||
FeatureFlag.FlexibleCollectionsV1,
|
FeatureFlag.FlexibleCollectionsV1,
|
||||||
);
|
);
|
||||||
@@ -114,7 +111,6 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
group: GroupView;
|
group: GroupView;
|
||||||
|
|
||||||
groupForm = this.formBuilder.group({
|
groupForm = this.formBuilder.group({
|
||||||
accessAll: [false],
|
|
||||||
name: ["", [Validators.required, Validators.maxLength(100)]],
|
name: ["", [Validators.required, Validators.maxLength(100)]],
|
||||||
externalId: this.formBuilder.control({ value: "", disabled: true }),
|
externalId: this.formBuilder.control({ value: "", disabled: true }),
|
||||||
members: [[] as AccessItemValue[]],
|
members: [[] as AccessItemValue[]],
|
||||||
@@ -188,7 +184,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
this.flexibleCollectionsV1Enabled$,
|
this.flexibleCollectionsV1Enabled$,
|
||||||
]).pipe(
|
]).pipe(
|
||||||
map(([organization, flexibleCollectionsV1Enabled]) => {
|
map(([organization, flexibleCollectionsV1Enabled]) => {
|
||||||
if (!flexibleCollectionsV1Enabled || !organization.flexibleCollections) {
|
if (!flexibleCollectionsV1Enabled) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +272,6 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
this.groupForm.patchValue({
|
this.groupForm.patchValue({
|
||||||
name: this.group.name,
|
name: this.group.name,
|
||||||
externalId: this.group.externalId,
|
externalId: this.group.externalId,
|
||||||
accessAll: this.group.accessAll,
|
|
||||||
members: this.group.members.map((m) => ({
|
members: this.group.members.map((m) => ({
|
||||||
id: m,
|
id: m,
|
||||||
type: AccessItemType.Member,
|
type: AccessItemType.Member,
|
||||||
@@ -328,12 +323,8 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const formValue = this.groupForm.value;
|
const formValue = this.groupForm.value;
|
||||||
groupView.name = formValue.name;
|
groupView.name = formValue.name;
|
||||||
groupView.accessAll = formValue.accessAll;
|
|
||||||
groupView.members = formValue.members?.map((m) => m.id) ?? [];
|
groupView.members = formValue.members?.map((m) => m.id) ?? [];
|
||||||
|
groupView.collections = formValue.collections.map((c) => convertToSelectionView(c));
|
||||||
if (!groupView.accessAll) {
|
|
||||||
groupView.collections = formValue.collections.map((c) => convertToSelectionView(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.groupService.save(groupView);
|
await this.groupService.save(groupView);
|
||||||
|
|
||||||
|
|||||||
@@ -74,12 +74,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<td bitCell (click)="edit(g, ModalTabType.Collections)" class="tw-cursor-pointer">
|
<td bitCell (click)="edit(g, ModalTabType.Collections)" class="tw-cursor-pointer">
|
||||||
<bit-badge-list
|
<bit-badge-list
|
||||||
*ngIf="!g.details.accessAll"
|
|
||||||
[items]="g.collectionNames"
|
[items]="g.collectionNames"
|
||||||
[maxItems]="3"
|
[maxItems]="3"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
></bit-badge-list>
|
></bit-badge-list>
|
||||||
<span *ngIf="g.details.accessAll">{{ "all" | i18n }}</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td bitCell>
|
<td bitCell>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -49,14 +49,6 @@
|
|||||||
<bit-label>{{ "user" | i18n }}</bit-label>
|
<bit-label>{{ "user" | i18n }}</bit-label>
|
||||||
<bit-hint>{{ "userDesc" | i18n }}</bit-hint>
|
<bit-hint>{{ "userDesc" | i18n }}</bit-hint>
|
||||||
</bit-radio-button>
|
</bit-radio-button>
|
||||||
<bit-radio-button
|
|
||||||
*ngIf="!organization.flexibleCollections"
|
|
||||||
id="userTypeManager"
|
|
||||||
[value]="organizationUserType.Manager"
|
|
||||||
>
|
|
||||||
<bit-label>{{ "manager" | i18n }}</bit-label>
|
|
||||||
<bit-hint>{{ "managerDesc" | i18n }}</bit-hint>
|
|
||||||
</bit-radio-button>
|
|
||||||
<bit-radio-button id="userTypeAdmin" [value]="organizationUserType.Admin">
|
<bit-radio-button id="userTypeAdmin" [value]="organizationUserType.Admin">
|
||||||
<bit-label>{{ "admin" | i18n }}</bit-label>
|
<bit-label>{{ "admin" | i18n }}</bit-label>
|
||||||
<bit-hint>{{ "adminDesc" | i18n }}</bit-hint>
|
<bit-hint>{{ "adminDesc" | i18n }}</bit-hint>
|
||||||
@@ -91,140 +83,64 @@
|
|||||||
</bit-radio-button>
|
</bit-radio-button>
|
||||||
</bit-radio-group>
|
</bit-radio-group>
|
||||||
<ng-container *ngIf="customUserTypeSelected">
|
<ng-container *ngIf="customUserTypeSelected">
|
||||||
<ng-container *ngIf="!organization.flexibleCollections; else customPermissionsFC">
|
<div class="tw-grid tw-grid-cols-12 tw-gap-4" [formGroup]="permissionsGroup">
|
||||||
<h3 bitTypography="h3">
|
<div class="tw-col-span-4">
|
||||||
{{ "permissions" | i18n }}
|
<bit-form-control>
|
||||||
</h3>
|
<input type="checkbox" bitCheckbox formControlName="accessEventLogs" />
|
||||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4" [formGroup]="permissionsGroup">
|
<bit-label>{{ "accessEventLogs" | i18n }}</bit-label>
|
||||||
<div class="tw-col-span-6">
|
</bit-form-control>
|
||||||
<div class="tw-mb-3">
|
<bit-form-control>
|
||||||
<bit-label class="tw-font-semibold">{{
|
<input type="checkbox" bitCheckbox formControlName="accessImportExport" />
|
||||||
"managerPermissions" | i18n
|
<bit-label>{{ "accessImportExport" | i18n }}</bit-label>
|
||||||
}}</bit-label>
|
</bit-form-control>
|
||||||
<hr class="tw-mb-2 tw-mr-2 tw-mt-0" />
|
<bit-form-control>
|
||||||
<app-nested-checkbox
|
<input type="checkbox" bitCheckbox formControlName="accessReports" />
|
||||||
parentId="manageAssignedCollections"
|
<bit-label>{{ "accessReports" | i18n }}</bit-label>
|
||||||
[checkboxes]="permissionsGroup.controls.manageAssignedCollectionsGroup"
|
</bit-form-control>
|
||||||
>
|
</div>
|
||||||
</app-nested-checkbox>
|
<div class="tw-col-span-4">
|
||||||
</div>
|
<app-nested-checkbox
|
||||||
</div>
|
parentId="manageAllCollections"
|
||||||
<div class="tw-col-span-6">
|
[checkboxes]="permissionsGroup.controls.manageAllCollectionsGroup"
|
||||||
<div class="tw-mb-3">
|
>
|
||||||
<bit-label class="tw-font-semibold">{{ "adminPermissions" | i18n }}</bit-label>
|
</app-nested-checkbox>
|
||||||
<hr class="tw-mb-2 tw-mr-2 tw-mt-0" />
|
</div>
|
||||||
<bit-form-control>
|
<div class="tw-col-span-4">
|
||||||
<input type="checkbox" bitCheckbox formControlName="accessEventLogs" />
|
<div class="tw-mb-3">
|
||||||
<bit-label>{{ "accessEventLogs" | i18n }}</bit-label>
|
<bit-form-control>
|
||||||
</bit-form-control>
|
<input type="checkbox" bitCheckbox formControlName="manageGroups" />
|
||||||
<bit-form-control>
|
<bit-label>{{ "manageGroups" | i18n }}</bit-label>
|
||||||
<input type="checkbox" bitCheckbox formControlName="accessImportExport" />
|
</bit-form-control>
|
||||||
<bit-label>{{ "accessImportExport" | i18n }}</bit-label>
|
<bit-form-control>
|
||||||
</bit-form-control>
|
<input type="checkbox" bitCheckbox formControlName="manageSso" />
|
||||||
<bit-form-control>
|
<bit-label>{{ "manageSso" | i18n }}</bit-label>
|
||||||
<input type="checkbox" bitCheckbox formControlName="accessReports" />
|
</bit-form-control>
|
||||||
<bit-label>{{ "accessReports" | i18n }}</bit-label>
|
<bit-form-control>
|
||||||
</bit-form-control>
|
<input type="checkbox" bitCheckbox formControlName="managePolicies" />
|
||||||
<app-nested-checkbox
|
<bit-label>{{ "managePolicies" | i18n }}</bit-label>
|
||||||
parentId="manageAllCollections"
|
</bit-form-control>
|
||||||
[checkboxes]="permissionsGroup.controls.manageAllCollectionsGroup"
|
<bit-form-control>
|
||||||
>
|
<input
|
||||||
</app-nested-checkbox>
|
id="manageUsers"
|
||||||
<bit-form-control>
|
type="checkbox"
|
||||||
<input type="checkbox" bitCheckbox formControlName="manageGroups" />
|
bitCheckbox
|
||||||
<bit-label>{{ "manageGroups" | i18n }}</bit-label>
|
formControlName="manageUsers"
|
||||||
</bit-form-control>
|
(change)="handleDependentPermissions()"
|
||||||
<bit-form-control>
|
/>
|
||||||
<input type="checkbox" bitCheckbox formControlName="manageSso" />
|
<bit-label>{{ "manageUsers" | i18n }}</bit-label>
|
||||||
<bit-label>{{ "manageSso" | i18n }}</bit-label>
|
</bit-form-control>
|
||||||
</bit-form-control>
|
<bit-form-control>
|
||||||
<bit-form-control>
|
<input
|
||||||
<input type="checkbox" bitCheckbox formControlName="managePolicies" />
|
type="checkbox"
|
||||||
<bit-label>{{ "managePolicies" | i18n }}</bit-label>
|
bitCheckbox
|
||||||
</bit-form-control>
|
formControlName="manageResetPassword"
|
||||||
<bit-form-control>
|
(change)="handleDependentPermissions()"
|
||||||
<input
|
/>
|
||||||
id="manageUsers"
|
<bit-label>{{ "manageAccountRecovery" | i18n }}</bit-label>
|
||||||
type="checkbox"
|
</bit-form-control>
|
||||||
bitCheckbox
|
|
||||||
formControlName="manageUsers"
|
|
||||||
(change)="handleDependentPermissions()"
|
|
||||||
/>
|
|
||||||
<bit-label>{{ "manageUsers" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
<bit-form-control>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
bitCheckbox
|
|
||||||
formControlName="manageResetPassword"
|
|
||||||
(change)="handleDependentPermissions()"
|
|
||||||
/>
|
|
||||||
<bit-label>{{ "manageAccountRecovery" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</div>
|
||||||
<ng-template #customPermissionsFC>
|
|
||||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4" [formGroup]="permissionsGroup">
|
|
||||||
<div class="tw-col-span-4">
|
|
||||||
<bit-form-control>
|
|
||||||
<input type="checkbox" bitCheckbox formControlName="accessEventLogs" />
|
|
||||||
<bit-label>{{ "accessEventLogs" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
<bit-form-control>
|
|
||||||
<input type="checkbox" bitCheckbox formControlName="accessImportExport" />
|
|
||||||
<bit-label>{{ "accessImportExport" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
<bit-form-control>
|
|
||||||
<input type="checkbox" bitCheckbox formControlName="accessReports" />
|
|
||||||
<bit-label>{{ "accessReports" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
</div>
|
|
||||||
<div class="tw-col-span-4">
|
|
||||||
<app-nested-checkbox
|
|
||||||
parentId="manageAllCollections"
|
|
||||||
[checkboxes]="permissionsGroup.controls.manageAllCollectionsGroup"
|
|
||||||
>
|
|
||||||
</app-nested-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="tw-col-span-4">
|
|
||||||
<div class="tw-mb-3">
|
|
||||||
<bit-form-control>
|
|
||||||
<input type="checkbox" bitCheckbox formControlName="manageGroups" />
|
|
||||||
<bit-label>{{ "manageGroups" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
<bit-form-control>
|
|
||||||
<input type="checkbox" bitCheckbox formControlName="manageSso" />
|
|
||||||
<bit-label>{{ "manageSso" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
<bit-form-control>
|
|
||||||
<input type="checkbox" bitCheckbox formControlName="managePolicies" />
|
|
||||||
<bit-label>{{ "managePolicies" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
<bit-form-control>
|
|
||||||
<input
|
|
||||||
id="manageUsers"
|
|
||||||
type="checkbox"
|
|
||||||
bitCheckbox
|
|
||||||
formControlName="manageUsers"
|
|
||||||
(change)="handleDependentPermissions()"
|
|
||||||
/>
|
|
||||||
<bit-label>{{ "manageUsers" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
<bit-form-control>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
bitCheckbox
|
|
||||||
formControlName="manageResetPassword"
|
|
||||||
(change)="handleDependentPermissions()"
|
|
||||||
/>
|
|
||||||
<bit-label>{{ "manageAccountRecovery" | i18n }}</bit-label>
|
|
||||||
</bit-form-control>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="organization.useSecretsManager">
|
<ng-container *ngIf="organization.useSecretsManager">
|
||||||
<h3 class="tw-mt-4">
|
<h3 class="tw-mt-4">
|
||||||
@@ -272,7 +188,6 @@
|
|||||||
[columnHeader]="'groups' | i18n"
|
[columnHeader]="'groups' | i18n"
|
||||||
[selectorLabelText]="'selectGroups' | i18n"
|
[selectorLabelText]="'selectGroups' | i18n"
|
||||||
[emptySelectionText]="'noGroupsAdded' | i18n"
|
[emptySelectionText]="'noGroupsAdded' | i18n"
|
||||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
|
||||||
[hideMultiSelect]="restrictEditingSelf$ | async"
|
[hideMultiSelect]="restrictEditingSelf$ | async"
|
||||||
></bit-access-selector>
|
></bit-access-selector>
|
||||||
</bit-tab>
|
</bit-tab>
|
||||||
@@ -294,26 +209,7 @@
|
|||||||
{{ "restrictedCollectionAssignmentDesc" | i18n }}
|
{{ "restrictedCollectionAssignmentDesc" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!organization.flexibleCollections" class="tw-mb-6">
|
|
||||||
<bit-form-control>
|
|
||||||
<input type="checkbox" bitCheckbox formControlName="accessAllCollections" />
|
|
||||||
<bit-label>
|
|
||||||
{{ "accessAllCollectionsDesc" | i18n }}
|
|
||||||
<a
|
|
||||||
bitLink
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
|
||||||
href="https://bitwarden.com/help/user-types-access-control/#access-control"
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
</bit-label>
|
|
||||||
<bit-hint>{{ "accessAllCollectionsHelp" | i18n }}</bit-hint>
|
|
||||||
</bit-form-control>
|
|
||||||
</div>
|
|
||||||
<bit-access-selector
|
<bit-access-selector
|
||||||
*ngIf="!accessAllCollections"
|
|
||||||
[permissionMode]="PermissionMode.Edit"
|
[permissionMode]="PermissionMode.Edit"
|
||||||
formControlName="access"
|
formControlName="access"
|
||||||
[showGroupColumn]="organization.useGroups"
|
[showGroupColumn]="organization.useGroups"
|
||||||
@@ -321,7 +217,6 @@
|
|||||||
[columnHeader]="'collection' | i18n"
|
[columnHeader]="'collection' | i18n"
|
||||||
[selectorLabelText]="'selectCollections' | i18n"
|
[selectorLabelText]="'selectCollections' | i18n"
|
||||||
[emptySelectionText]="'noCollectionsAdded' | i18n"
|
[emptySelectionText]="'noCollectionsAdded' | i18n"
|
||||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
|
||||||
[hideMultiSelect]="restrictEditingSelf$ | async"
|
[hideMultiSelect]="restrictEditingSelf$ | async"
|
||||||
></bit-access-selector
|
></bit-access-selector
|
||||||
></bit-tab>
|
></bit-tab>
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
emails: [""],
|
emails: [""],
|
||||||
type: OrganizationUserType.User,
|
type: OrganizationUserType.User,
|
||||||
externalId: this.formBuilder.control({ value: "", disabled: true }),
|
externalId: this.formBuilder.control({ value: "", disabled: true }),
|
||||||
accessAllCollections: false,
|
|
||||||
accessSecretsManager: false,
|
accessSecretsManager: false,
|
||||||
access: [[] as AccessItemValue[]],
|
access: [[] as AccessItemValue[]],
|
||||||
groups: [[] as AccessItemValue[]],
|
groups: [[] as AccessItemValue[]],
|
||||||
@@ -110,11 +109,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
protected canAssignAccessToAnyCollection$: Observable<boolean>;
|
protected canAssignAccessToAnyCollection$: Observable<boolean>;
|
||||||
|
|
||||||
protected permissionsGroup = this.formBuilder.group({
|
protected permissionsGroup = this.formBuilder.group({
|
||||||
manageAssignedCollectionsGroup: this.formBuilder.group<Record<string, boolean>>({
|
|
||||||
manageAssignedCollections: false,
|
|
||||||
editAssignedCollections: false,
|
|
||||||
deleteAssignedCollections: false,
|
|
||||||
}),
|
|
||||||
manageAllCollectionsGroup: this.formBuilder.group<Record<string, boolean>>({
|
manageAllCollectionsGroup: this.formBuilder.group<Record<string, boolean>>({
|
||||||
manageAllCollections: false,
|
manageAllCollections: false,
|
||||||
createNewCollections: false,
|
createNewCollections: false,
|
||||||
@@ -137,10 +131,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
return this.formGroup.value.type === OrganizationUserType.Custom;
|
return this.formGroup.value.type === OrganizationUserType.Custom;
|
||||||
}
|
}
|
||||||
|
|
||||||
get accessAllCollections(): boolean {
|
|
||||||
return this.formGroup.value.accessAllCollections;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DIALOG_DATA) protected params: MemberDialogParams,
|
@Inject(DIALOG_DATA) protected params: MemberDialogParams,
|
||||||
private dialogRef: DialogRef<MemberDialogResult>,
|
private dialogRef: DialogRef<MemberDialogResult>,
|
||||||
@@ -189,7 +179,7 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
|
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
map(([organization, flexibleCollectionsV1Enabled]) => {
|
map(([organization, flexibleCollectionsV1Enabled]) => {
|
||||||
if (!flexibleCollectionsV1Enabled || !organization.flexibleCollections) {
|
if (!flexibleCollectionsV1Enabled) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,13 +306,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
this.showNoMasterPasswordWarning =
|
this.showNoMasterPasswordWarning =
|
||||||
userDetails.status > OrganizationUserStatusType.Invited &&
|
userDetails.status > OrganizationUserStatusType.Invited &&
|
||||||
userDetails.hasMasterPassword === false;
|
userDetails.hasMasterPassword === false;
|
||||||
const assignedCollectionsPermissions = {
|
|
||||||
editAssignedCollections: userDetails.permissions.editAssignedCollections,
|
|
||||||
deleteAssignedCollections: userDetails.permissions.deleteAssignedCollections,
|
|
||||||
manageAssignedCollections:
|
|
||||||
userDetails.permissions.editAssignedCollections &&
|
|
||||||
userDetails.permissions.deleteAssignedCollections,
|
|
||||||
};
|
|
||||||
const allCollectionsPermissions = {
|
const allCollectionsPermissions = {
|
||||||
createNewCollections: userDetails.permissions.createNewCollections,
|
createNewCollections: userDetails.permissions.createNewCollections,
|
||||||
editAnyCollection: userDetails.permissions.editAnyCollection,
|
editAnyCollection: userDetails.permissions.editAnyCollection,
|
||||||
@@ -342,7 +325,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
managePolicies: userDetails.permissions.managePolicies,
|
managePolicies: userDetails.permissions.managePolicies,
|
||||||
manageUsers: userDetails.permissions.manageUsers,
|
manageUsers: userDetails.permissions.manageUsers,
|
||||||
manageResetPassword: userDetails.permissions.manageResetPassword,
|
manageResetPassword: userDetails.permissions.manageResetPassword,
|
||||||
manageAssignedCollectionsGroup: assignedCollectionsPermissions,
|
|
||||||
manageAllCollectionsGroup: allCollectionsPermissions,
|
manageAllCollectionsGroup: allCollectionsPermissions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -378,7 +360,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
this.formGroup.patchValue({
|
this.formGroup.patchValue({
|
||||||
type: userDetails.type,
|
type: userDetails.type,
|
||||||
externalId: userDetails.externalId,
|
externalId: userDetails.externalId,
|
||||||
accessAllCollections: userDetails.accessAll,
|
|
||||||
access: accessSelections,
|
access: accessSelections,
|
||||||
accessSecretsManager: userDetails.accessSecretsManager,
|
accessSecretsManager: userDetails.accessSecretsManager,
|
||||||
groups: groupAccessSelections,
|
groups: groupAccessSelections,
|
||||||
@@ -414,10 +395,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
editAnyCollection: this.permissionsGroup.value.manageAllCollectionsGroup.editAnyCollection,
|
editAnyCollection: this.permissionsGroup.value.manageAllCollectionsGroup.editAnyCollection,
|
||||||
deleteAnyCollection:
|
deleteAnyCollection:
|
||||||
this.permissionsGroup.value.manageAllCollectionsGroup.deleteAnyCollection,
|
this.permissionsGroup.value.manageAllCollectionsGroup.deleteAnyCollection,
|
||||||
editAssignedCollections:
|
|
||||||
this.permissionsGroup.value.manageAssignedCollectionsGroup.editAssignedCollections,
|
|
||||||
deleteAssignedCollections:
|
|
||||||
this.permissionsGroup.value.manageAssignedCollectionsGroup.deleteAssignedCollections,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return Object.assign(p, partialPermissions);
|
return Object.assign(p, partialPermissions);
|
||||||
@@ -467,7 +444,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
const userView = new OrganizationUserAdminView();
|
const userView = new OrganizationUserAdminView();
|
||||||
userView.id = this.params.organizationUserId;
|
userView.id = this.params.organizationUserId;
|
||||||
userView.organizationId = this.params.organizationId;
|
userView.organizationId = this.params.organizationId;
|
||||||
userView.accessAll = this.accessAllCollections;
|
|
||||||
userView.type = this.formGroup.value.type;
|
userView.type = this.formGroup.value.type;
|
||||||
userView.permissions = this.setRequestPermissions(
|
userView.permissions = this.setRequestPermissions(
|
||||||
userView.permissions ?? new PermissionsApi(),
|
userView.permissions ?? new PermissionsApi(),
|
||||||
|
|||||||
@@ -190,12 +190,10 @@
|
|||||||
class="tw-cursor-pointer"
|
class="tw-cursor-pointer"
|
||||||
>
|
>
|
||||||
<bit-badge-list
|
<bit-badge-list
|
||||||
*ngIf="organization.useGroups || !u.accessAll"
|
|
||||||
[items]="organization.useGroups ? u.groupNames : u.collectionNames"
|
[items]="organization.useGroups ? u.groupNames : u.collectionNames"
|
||||||
[maxItems]="3"
|
[maxItems]="3"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
></bit-badge-list>
|
></bit-badge-list>
|
||||||
<span *ngIf="!organization.useGroups && u.accessAll">{{ "all" | i18n }}</span>
|
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td
|
<td
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<form
|
<form
|
||||||
*ngIf="org && !loading && org.flexibleCollections"
|
*ngIf="org && !loading"
|
||||||
[bitSubmit]="submitCollectionManagement"
|
[bitSubmit]="submitCollectionManagement"
|
||||||
[formGroup]="collectionManagementFormGroup"
|
[formGroup]="collectionManagementFormGroup"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -110,15 +110,6 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #readOnlyPerm>
|
<ng-template #readOnlyPerm>
|
||||||
<div
|
|
||||||
*ngIf="item.accessAllItems"
|
|
||||||
class="tw-max-w-40 tw-overflow-hidden tw-overflow-ellipsis tw-whitespace-nowrap tw-border tw-border-solid tw-border-transparent tw-font-bold tw-text-muted"
|
|
||||||
[appA11yTitle]="accessAllLabelId(item) | i18n"
|
|
||||||
>
|
|
||||||
{{ "canEdit" | i18n }}
|
|
||||||
<i class="bwi bwi-filter tw-ml-1" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
*ngIf="item.readonly || disabled"
|
*ngIf="item.readonly || disabled"
|
||||||
class="tw-max-w-40 tw-overflow-hidden tw-overflow-ellipsis tw-whitespace-nowrap tw-font-bold tw-text-muted"
|
class="tw-max-w-40 tw-overflow-hidden tw-overflow-ellipsis tw-whitespace-nowrap tw-font-bold tw-text-muted"
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On
|
|||||||
|
|
||||||
// The enable() above also enables the permission control, so we need to disable it again
|
// The enable() above also enables the permission control, so we need to disable it again
|
||||||
// Disable permission control if accessAllItems is enabled or not in Edit mode
|
// Disable permission control if accessAllItems is enabled or not in Edit mode
|
||||||
if (item.accessAllItems || this.permissionMode != PermissionMode.Edit) {
|
if (this.permissionMode != PermissionMode.Edit) {
|
||||||
controlRow.controls.permission.disable();
|
controlRow.controls.permission.disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,21 +196,11 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On
|
|||||||
*/
|
*/
|
||||||
@Input() showGroupColumn: boolean;
|
@Input() showGroupColumn: boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable Flexible Collections changes (feature flag)
|
|
||||||
*/
|
|
||||||
@Input() set flexibleCollectionsEnabled(value: boolean) {
|
|
||||||
this._flexibleCollectionsEnabled = value;
|
|
||||||
this.permissionList = getPermissionList(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the multi-select so that new items cannot be added
|
* Hide the multi-select so that new items cannot be added
|
||||||
*/
|
*/
|
||||||
@Input() hideMultiSelect = false;
|
@Input() hideMultiSelect = false;
|
||||||
|
|
||||||
private _flexibleCollectionsEnabled: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly formBuilder: FormBuilder,
|
private readonly formBuilder: FormBuilder,
|
||||||
private readonly i18nService: I18nService,
|
private readonly i18nService: I18nService,
|
||||||
@@ -275,7 +265,7 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.permissionList = getPermissionList(this._flexibleCollectionsEnabled);
|
this.permissionList = getPermissionList();
|
||||||
// Watch the internal formArray for changes and propagate them
|
// Watch the internal formArray for changes and propagate them
|
||||||
this.selectionList.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((v) => {
|
this.selectionList.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((v) => {
|
||||||
if (!this.notifyOnChange || this.pauseChangeNotification) {
|
if (!this.notifyOnChange || this.pauseChangeNotification) {
|
||||||
@@ -328,12 +318,8 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On
|
|||||||
return this.permissionList.find((p) => p.perm == perm)?.labelId;
|
return this.permissionList.find((p) => p.perm == perm)?.labelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected accessAllLabelId(item: AccessItemView) {
|
|
||||||
return item.type == AccessItemType.Group ? "groupAccessAll" : "memberAccessAll";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected canEditItemPermission(item: AccessItemView) {
|
protected canEditItemPermission(item: AccessItemView) {
|
||||||
return this.permissionMode == PermissionMode.Edit && !item.readonly && !item.accessAllItems;
|
return this.permissionMode == PermissionMode.Edit && !item.readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _itemComparator(a: AccessItemView, b: AccessItemView) {
|
private _itemComparator(a: AccessItemView, b: AccessItemView) {
|
||||||
|
|||||||
@@ -34,12 +34,6 @@ export enum AccessItemType {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type AccessItemView = SelectItemView & {
|
export type AccessItemView = SelectItemView & {
|
||||||
/**
|
|
||||||
* Flag that this group/member can access all items.
|
|
||||||
* This will disable the permission editor for this item.
|
|
||||||
*/
|
|
||||||
accessAllItems?: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag that this item cannot be modified.
|
* Flag that this item cannot be modified.
|
||||||
* This will disable the permission editor and will keep
|
* This will disable the permission editor and will keep
|
||||||
@@ -82,16 +76,14 @@ export type Permission = {
|
|||||||
labelId: string;
|
labelId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPermissionList = (flexibleCollectionsEnabled: boolean): Permission[] => {
|
export const getPermissionList = (): Permission[] => {
|
||||||
const permissions = [
|
const permissions = [
|
||||||
{ perm: CollectionPermission.View, labelId: "canView" },
|
{ perm: CollectionPermission.View, labelId: "canView" },
|
||||||
{ perm: CollectionPermission.ViewExceptPass, labelId: "canViewExceptPass" },
|
{ perm: CollectionPermission.ViewExceptPass, labelId: "canViewExceptPass" },
|
||||||
{ perm: CollectionPermission.Edit, labelId: "canEdit" },
|
{ perm: CollectionPermission.Edit, labelId: "canEdit" },
|
||||||
{ perm: CollectionPermission.EditExceptPass, labelId: "canEditExceptPass" },
|
{ perm: CollectionPermission.EditExceptPass, labelId: "canEditExceptPass" },
|
||||||
|
{ perm: CollectionPermission.Manage, labelId: "canManage" },
|
||||||
];
|
];
|
||||||
if (flexibleCollectionsEnabled) {
|
|
||||||
permissions.push({ perm: CollectionPermission.Manage, labelId: "canManage" });
|
|
||||||
}
|
|
||||||
|
|
||||||
return permissions;
|
return permissions;
|
||||||
};
|
};
|
||||||
@@ -142,8 +134,6 @@ export function mapGroupToAccessItemView(group: GroupView): AccessItemView {
|
|||||||
type: AccessItemType.Group,
|
type: AccessItemType.Group,
|
||||||
listName: group.name,
|
listName: group.name,
|
||||||
labelName: group.name,
|
labelName: group.name,
|
||||||
accessAllItems: group.accessAll,
|
|
||||||
readonly: group.accessAll,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +147,5 @@ export function mapUserToAccessItemView(user: OrganizationUserUserDetailsRespons
|
|||||||
listName: user.name?.length > 0 ? `${user.name} (${user.email})` : user.email,
|
listName: user.name?.length > 0 ? `${user.name} (${user.email})` : user.email,
|
||||||
labelName: user.name ?? user.email,
|
labelName: user.name ?? user.email,
|
||||||
status: user.status,
|
status: user.status,
|
||||||
accessAllItems: user.accessAll,
|
|
||||||
readonly: user.accessAll,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,7 +253,6 @@ MemberGroupAccess.args = {
|
|||||||
type: AccessItemType.Group,
|
type: AccessItemType.Group,
|
||||||
listName: "Admin Group",
|
listName: "Admin Group",
|
||||||
labelName: "Admin Group",
|
labelName: "Admin Group",
|
||||||
accessAllItems: true,
|
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
@@ -309,7 +308,6 @@ CollectionAccess.args = {
|
|||||||
type: AccessItemType.Group,
|
type: AccessItemType.Group,
|
||||||
listName: "Admin Group",
|
listName: "Admin Group",
|
||||||
labelName: "Admin Group",
|
labelName: "Admin Group",
|
||||||
accessAllItems: true,
|
|
||||||
readonly: true,
|
readonly: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -320,7 +318,6 @@ CollectionAccess.args = {
|
|||||||
status: OrganizationUserStatusType.Confirmed,
|
status: OrganizationUserStatusType.Confirmed,
|
||||||
role: OrganizationUserType.Admin,
|
role: OrganizationUserType.Admin,
|
||||||
email: "admin@email.com",
|
email: "admin@email.com",
|
||||||
accessAllItems: true,
|
|
||||||
readonly: true,
|
readonly: true,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ export class UserTypePipe implements PipeTransform {
|
|||||||
return this.i18nService.t("admin");
|
return this.i18nService.t("admin");
|
||||||
case OrganizationUserType.User:
|
case OrganizationUserType.User:
|
||||||
return this.i18nService.t("user");
|
return this.i18nService.t("user");
|
||||||
case OrganizationUserType.Manager:
|
|
||||||
return this.i18nService.t("manager");
|
|
||||||
case OrganizationUserType.Custom:
|
case OrganizationUserType.Custom:
|
||||||
return this.i18nService.t("custom");
|
return this.i18nService.t("custom");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,33 @@
|
|||||||
<div class="mt-5 d-flex justify-content-center" *ngIf="loading">
|
<div *ngIf="loading" class="tw-text-center">
|
||||||
<div>
|
<i
|
||||||
<img class="mb-4 logo logo-themed" alt="Bitwarden" />
|
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
|
||||||
<p class="text-center">
|
title="{{ 'loading' | i18n }}"
|
||||||
<i
|
aria-hidden="true"
|
||||||
class="bwi bwi-spinner bwi-spin bwi-2x text-muted"
|
></i>
|
||||||
title="{{ 'loading' | i18n }}"
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="container" *ngIf="!loading">
|
|
||||||
<div class="row justify-content-md-center mt-5">
|
|
||||||
<div class="col-5">
|
|
||||||
<p class="lead text-center mb-4">{{ "removeMasterPassword" | i18n }}</p>
|
|
||||||
<hr />
|
|
||||||
<div class="card d-block">
|
|
||||||
<div class="card-body">
|
|
||||||
<p>{{ "convertOrganizationEncryptionDesc" | i18n: organization.name }}</p>
|
|
||||||
|
|
||||||
<button
|
<div *ngIf="!loading">
|
||||||
type="button"
|
<p>{{ "convertOrganizationEncryptionDesc" | i18n: organization.name }}</p>
|
||||||
class="btn btn-primary btn-block"
|
|
||||||
(click)="convert()"
|
<button
|
||||||
[disabled]="actionPromise"
|
bitButton
|
||||||
>
|
type="button"
|
||||||
<i
|
buttonType="primary"
|
||||||
class="bwi bwi-spinner bwi-spin"
|
class="tw-w-full"
|
||||||
title="{{ 'loading' | i18n }}"
|
[bitAction]="convert"
|
||||||
aria-hidden="true"
|
[block]="true"
|
||||||
*ngIf="continuing"
|
>
|
||||||
></i>
|
{{ "removeMasterPassword" | i18n }}
|
||||||
{{ "removeMasterPassword" | i18n }}
|
</button>
|
||||||
</button>
|
<button
|
||||||
<button
|
bitButton
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-outline-secondary btn-block"
|
buttonType="secondary"
|
||||||
(click)="leave()"
|
class="tw-w-full"
|
||||||
[disabled]="actionPromise"
|
[bitAction]="leave"
|
||||||
>
|
[block]="true"
|
||||||
<i
|
>
|
||||||
class="bwi bwi-spinner bwi-spin"
|
{{ "leaveOrganization" | i18n }}
|
||||||
title="{{ 'loading' | i18n }}"
|
</button>
|
||||||
aria-hidden="true"
|
|
||||||
*ngIf="leaving"
|
|
||||||
></i>
|
|
||||||
{{ "leaveOrganization" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -176,12 +176,6 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
data: { titleId: "updatePassword" } satisfies DataProperties,
|
data: { titleId: "updatePassword" } satisfies DataProperties,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "remove-password",
|
|
||||||
component: RemovePasswordComponent,
|
|
||||||
canActivate: [AuthGuard],
|
|
||||||
data: { titleId: "removeMasterPassword" } satisfies DataProperties,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "migrate-legacy-encryption",
|
path: "migrate-legacy-encryption",
|
||||||
loadComponent: () =>
|
loadComponent: () =>
|
||||||
@@ -195,25 +189,6 @@ const routes: Routes = [
|
|||||||
path: "",
|
path: "",
|
||||||
component: AnonLayoutWrapperComponent,
|
component: AnonLayoutWrapperComponent,
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
path: "recover-2fa",
|
|
||||||
canActivate: [unauthGuardFn()],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: "",
|
|
||||||
component: RecoverTwoFactorComponent,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "",
|
|
||||||
component: EnvironmentSelectorComponent,
|
|
||||||
outlet: "environment-selector",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
data: {
|
|
||||||
pageTitle: "recoverAccountTwoStep",
|
|
||||||
titleId: "recoverAccountTwoStep",
|
|
||||||
} satisfies DataProperties & AnonLayoutWrapperData,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "accept-emergency",
|
path: "accept-emergency",
|
||||||
canActivate: [deepLinkGuard()],
|
canActivate: [deepLinkGuard()],
|
||||||
@@ -237,6 +212,34 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "recover-2fa",
|
||||||
|
canActivate: [unauthGuardFn()],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: RecoverTwoFactorComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: EnvironmentSelectorComponent,
|
||||||
|
outlet: "environment-selector",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: {
|
||||||
|
pageTitle: "recoverAccountTwoStep",
|
||||||
|
titleId: "recoverAccountTwoStep",
|
||||||
|
} satisfies DataProperties & AnonLayoutWrapperData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "remove-password",
|
||||||
|
component: RemovePasswordComponent,
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
data: {
|
||||||
|
pageTitle: "removeMasterPassword",
|
||||||
|
titleId: "removeMasterPassword",
|
||||||
|
} satisfies DataProperties & AnonLayoutWrapperData,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
</bit-tab>
|
</bit-tab>
|
||||||
<bit-tab label="{{ 'access' | i18n }}">
|
<bit-tab label="{{ 'access' | i18n }}">
|
||||||
<div class="tw-mb-3" *ngIf="organization.flexibleCollections">
|
<div class="tw-mb-3">
|
||||||
<ng-container *ngIf="dialogReadonly">
|
<ng-container *ngIf="dialogReadonly">
|
||||||
<span>{{ "readOnlyCollectionAccess" | i18n }}</span>
|
<span>{{ "readOnlyCollectionAccess" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -107,7 +107,6 @@
|
|||||||
[selectorLabelText]="'selectGroupsAndMembers' | i18n"
|
[selectorLabelText]="'selectGroupsAndMembers' | i18n"
|
||||||
[selectorHelpText]="'userPermissionOverrideHelperDesc' | i18n"
|
[selectorHelpText]="'userPermissionOverrideHelperDesc' | i18n"
|
||||||
[emptySelectionText]="'noMembersOrGroupsAdded' | i18n"
|
[emptySelectionText]="'noMembersOrGroupsAdded' | i18n"
|
||||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
|
||||||
></bit-access-selector>
|
></bit-access-selector>
|
||||||
<bit-access-selector
|
<bit-access-selector
|
||||||
*ngIf="!organization.useGroups"
|
*ngIf="!organization.useGroups"
|
||||||
@@ -117,7 +116,6 @@
|
|||||||
[columnHeader]="'memberColumnHeader' | i18n"
|
[columnHeader]="'memberColumnHeader' | i18n"
|
||||||
[selectorLabelText]="'selectMembers' | i18n"
|
[selectorLabelText]="'selectMembers' | i18n"
|
||||||
[emptySelectionText]="'noMembersAdded' | i18n"
|
[emptySelectionText]="'noMembersAdded' | i18n"
|
||||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
|
||||||
></bit-access-selector>
|
></bit-access-selector>
|
||||||
</bit-tab>
|
</bit-tab>
|
||||||
</bit-tab-group>
|
</bit-tab-group>
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
(u) => u.userId === this.organization?.userId,
|
(u) => u.userId === this.organization?.userId,
|
||||||
)?.id;
|
)?.id;
|
||||||
const initialSelection: AccessItemValue[] =
|
const initialSelection: AccessItemValue[] =
|
||||||
currentOrgUserId !== undefined && organization.flexibleCollections
|
currentOrgUserId !== undefined
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
id: currentOrgUserId,
|
id: currentOrgUserId,
|
||||||
@@ -239,11 +239,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (flexibleCollectionsV1 && !organization.allowAdminAccessToAllCollectionItems) {
|
||||||
organization.flexibleCollections &&
|
|
||||||
flexibleCollectionsV1 &&
|
|
||||||
!organization.allowAdminAccessToAllCollectionItems
|
|
||||||
) {
|
|
||||||
this.formGroup.controls.access.addValidators(validateCanManagePermission);
|
this.formGroup.controls.access.addValidators(validateCanManagePermission);
|
||||||
} else {
|
} else {
|
||||||
this.formGroup.controls.access.removeValidators(validateCanManagePermission);
|
this.formGroup.controls.access.removeValidators(validateCanManagePermission);
|
||||||
@@ -444,8 +440,7 @@ function mapGroupToAccessItemView(group: GroupView, collectionId: string): Acces
|
|||||||
type: AccessItemType.Group,
|
type: AccessItemType.Group,
|
||||||
listName: group.name,
|
listName: group.name,
|
||||||
labelName: group.name,
|
labelName: group.name,
|
||||||
accessAllItems: group.accessAll,
|
readonly: false,
|
||||||
readonly: group.accessAll,
|
|
||||||
readonlyPermission:
|
readonlyPermission:
|
||||||
collectionId != null
|
collectionId != null
|
||||||
? convertToPermission(group.collections.find((gc) => gc.id == collectionId))
|
? convertToPermission(group.collections.find((gc) => gc.id == collectionId))
|
||||||
@@ -471,8 +466,7 @@ function mapUserToAccessItemView(
|
|||||||
listName: user.name?.length > 0 ? `${user.name} (${user.email})` : user.email,
|
listName: user.name?.length > 0 ? `${user.name} (${user.email})` : user.email,
|
||||||
labelName: user.name ?? user.email,
|
labelName: user.name ?? user.email,
|
||||||
status: user.status,
|
status: user.status,
|
||||||
accessAllItems: user.accessAll,
|
readonly: false,
|
||||||
readonly: user.accessAll,
|
|
||||||
readonlyPermission:
|
readonlyPermission:
|
||||||
collectionId != null
|
collectionId != null
|
||||||
? convertToPermission(
|
? convertToPermission(
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export class VaultCollectionRowComponent {
|
|||||||
return this.i18nService.t("canEdit");
|
return this.i18nService.t("canEdit");
|
||||||
}
|
}
|
||||||
if ((this.collection as CollectionAdminView).assigned) {
|
if ((this.collection as CollectionAdminView).assigned) {
|
||||||
const permissionList = getPermissionList(this.organization?.flexibleCollections);
|
const permissionList = getPermissionList();
|
||||||
return this.i18nService.t(
|
return this.i18nService.t(
|
||||||
permissionList.find((p) => p.perm === convertToPermission(this.collection))?.labelId,
|
permissionList.find((p) => p.perm === convertToPermission(this.collection))?.labelId,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { VaultFilter } from "../models/vault-filter.model";
|
|||||||
})
|
})
|
||||||
export class VaultFilterSectionComponent implements OnInit, OnDestroy {
|
export class VaultFilterSectionComponent implements OnInit, OnDestroy {
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
protected flexibleCollectionsEnabled: boolean;
|
|
||||||
|
|
||||||
@Input() activeFilter: VaultFilter;
|
@Input() activeFilter: VaultFilter;
|
||||||
@Input() section: VaultFilterSection;
|
@Input() section: VaultFilterSection;
|
||||||
@@ -40,12 +39,6 @@ export class VaultFilterSectionComponent implements OnInit, OnDestroy {
|
|||||||
this.section?.data$?.pipe(takeUntil(this.destroy$)).subscribe((data) => {
|
this.section?.data$?.pipe(takeUntil(this.destroy$)).subscribe((data) => {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
});
|
});
|
||||||
this.vaultFilterService
|
|
||||||
.getOrganizationFilter()
|
|
||||||
.pipe(takeUntil(this.destroy$))
|
|
||||||
.subscribe((org) => {
|
|
||||||
this.flexibleCollectionsEnabled = org != null ? org.flexibleCollections : false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@@ -77,10 +70,9 @@ export class VaultFilterSectionComponent implements OnInit, OnDestroy {
|
|||||||
const { organizationId, cipherTypeId, folderId, collectionId, isCollectionSelected } =
|
const { organizationId, cipherTypeId, folderId, collectionId, isCollectionSelected } =
|
||||||
this.activeFilter;
|
this.activeFilter;
|
||||||
|
|
||||||
const collectionStatus = this.flexibleCollectionsEnabled
|
const collectionStatus =
|
||||||
? filterNode?.node.id === "AllCollections" &&
|
filterNode?.node.id === "AllCollections" &&
|
||||||
(isCollectionSelected || collectionId === "AllCollections")
|
(isCollectionSelected || collectionId === "AllCollections");
|
||||||
: collectionId === filterNode?.node.id;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
organizationId === filterNode?.node.id ||
|
organizationId === filterNode?.node.id ||
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
[selectorLabelText]="'selectGroupsAndMembers' | i18n"
|
[selectorLabelText]="'selectGroupsAndMembers' | i18n"
|
||||||
[selectorHelpText]="'userPermissionOverrideHelperDesc' | i18n"
|
[selectorHelpText]="'userPermissionOverrideHelperDesc' | i18n"
|
||||||
[emptySelectionText]="'noMembersOrGroupsAdded' | i18n"
|
[emptySelectionText]="'noMembersOrGroupsAdded' | i18n"
|
||||||
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async"
|
|
||||||
></bit-access-selector>
|
></bit-access-selector>
|
||||||
<bit-access-selector
|
<bit-access-selector
|
||||||
*ngIf="!organization?.useGroups"
|
*ngIf="!organization?.useGroups"
|
||||||
@@ -27,7 +26,6 @@
|
|||||||
[columnHeader]="'memberColumnHeader' | i18n"
|
[columnHeader]="'memberColumnHeader' | i18n"
|
||||||
[selectorLabelText]="'selectMembers' | i18n"
|
[selectorLabelText]="'selectMembers' | i18n"
|
||||||
[emptySelectionText]="'noMembersAdded' | i18n"
|
[emptySelectionText]="'noMembersAdded' | i18n"
|
||||||
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async"
|
|
||||||
></bit-access-selector>
|
></bit-access-selector>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
import { Component, Inject, OnDestroy } from "@angular/core";
|
import { Component, Inject, OnDestroy } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { combineLatest, map, of, Subject, switchMap, takeUntil } from "rxjs";
|
import { combineLatest, of, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
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 { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||||
@@ -42,10 +42,6 @@ export enum BulkCollectionsDialogResult {
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
export class BulkCollectionsDialogComponent implements OnDestroy {
|
export class BulkCollectionsDialogComponent implements OnDestroy {
|
||||||
protected flexibleCollectionsEnabled$ = this.organizationService
|
|
||||||
.get$(this.params.organizationId)
|
|
||||||
.pipe(map((o) => o?.flexibleCollections));
|
|
||||||
|
|
||||||
protected readonly PermissionMode = PermissionMode;
|
protected readonly PermissionMode = PermissionMode;
|
||||||
|
|
||||||
protected formGroup = this.formBuilder.group({
|
protected formGroup = this.formBuilder.group({
|
||||||
|
|||||||
@@ -103,11 +103,7 @@ export class VaultFilterComponent extends BaseVaultFilterComponent implements On
|
|||||||
async buildAllFilters(): Promise<VaultFilterList> {
|
async buildAllFilters(): Promise<VaultFilterList> {
|
||||||
const builderFilter = {} as VaultFilterList;
|
const builderFilter = {} as VaultFilterList;
|
||||||
builderFilter.typeFilter = await this.addTypeFilter(["favorites"]);
|
builderFilter.typeFilter = await this.addTypeFilter(["favorites"]);
|
||||||
if (this._organization?.flexibleCollections) {
|
builderFilter.collectionFilter = await this.addCollectionFilter();
|
||||||
builderFilter.collectionFilter = await this.addCollectionFilter();
|
|
||||||
} else {
|
|
||||||
builderFilter.collectionFilter = await super.addCollectionFilter();
|
|
||||||
}
|
|
||||||
builderFilter.trashFilter = await this.addTrashFilter();
|
builderFilter.trashFilter = await this.addTrashFilter();
|
||||||
return builderFilter;
|
return builderFilter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,7 @@
|
|||||||
queryParamsHandling="merge"
|
queryParamsHandling="merge"
|
||||||
>
|
>
|
||||||
{{ organization.name }}
|
{{ organization.name }}
|
||||||
<span *ngIf="!organization.flexibleCollections">
|
<span>
|
||||||
{{ "vault" | i18n | lowercase }}
|
|
||||||
</span>
|
|
||||||
<span *ngIf="organization.flexibleCollections">
|
|
||||||
{{ "collections" | i18n | lowercase }}
|
{{ "collections" | i18n | lowercase }}
|
||||||
</span>
|
</span>
|
||||||
</bit-breadcrumb>
|
</bit-breadcrumb>
|
||||||
|
|||||||
@@ -89,9 +89,7 @@ export class VaultHeaderComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
const headerType = this.organization?.flexibleCollections
|
const headerType = this.i18nService.t("collections").toLowerCase();
|
||||||
? this.i18nService.t("collections").toLowerCase()
|
|
||||||
: this.i18nService.t("vault").toLowerCase();
|
|
||||||
|
|
||||||
if (this.collection != null) {
|
if (this.collection != null) {
|
||||||
return this.collection.node.name;
|
return this.collection.node.name;
|
||||||
|
|||||||
@@ -65,8 +65,8 @@
|
|||||||
[useEvents]="organization?.canAccessEventLogs"
|
[useEvents]="organization?.canAccessEventLogs"
|
||||||
[showAdminActions]="true"
|
[showAdminActions]="true"
|
||||||
(onEvent)="onVaultItemsEvent($event)"
|
(onEvent)="onVaultItemsEvent($event)"
|
||||||
[showBulkEditCollectionAccess]="organization?.flexibleCollections"
|
[showBulkEditCollectionAccess]="true"
|
||||||
[showBulkAddToCollections]="organization?.flexibleCollections"
|
[showBulkAddToCollections]="true"
|
||||||
[viewingOrgVault]="true"
|
[viewingOrgVault]="true"
|
||||||
[flexibleCollectionsV1Enabled]="flexibleCollectionsV1Enabled"
|
[flexibleCollectionsV1Enabled]="flexibleCollectionsV1Enabled"
|
||||||
[addAccessStatus]="addAccessStatus$ | async"
|
[addAccessStatus]="addAccessStatus$ | async"
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
private _flexibleCollectionsV1FlagEnabled: boolean;
|
private _flexibleCollectionsV1FlagEnabled: boolean;
|
||||||
|
|
||||||
protected get flexibleCollectionsV1Enabled(): boolean {
|
protected get flexibleCollectionsV1Enabled(): boolean {
|
||||||
return this._flexibleCollectionsV1FlagEnabled && this.organization?.flexibleCollections;
|
return this._flexibleCollectionsV1FlagEnabled;
|
||||||
}
|
}
|
||||||
protected orgRevokedUsers: OrganizationUserUserDetailsResponse[];
|
protected orgRevokedUsers: OrganizationUserUserDetailsResponse[];
|
||||||
|
|
||||||
|
|||||||
@@ -2794,12 +2794,6 @@
|
|||||||
"userDesc": {
|
"userDesc": {
|
||||||
"message": "Access and add items to assigned collections"
|
"message": "Access and add items to assigned collections"
|
||||||
},
|
},
|
||||||
"manager": {
|
|
||||||
"message": "Manager"
|
|
||||||
},
|
|
||||||
"managerDesc": {
|
|
||||||
"message": "Create, delete, and manage access in assigned collections"
|
|
||||||
},
|
|
||||||
"all": {
|
"all": {
|
||||||
"message": "All"
|
"message": "All"
|
||||||
},
|
},
|
||||||
@@ -4576,12 +4570,6 @@
|
|||||||
"permission": {
|
"permission": {
|
||||||
"message": "Permission"
|
"message": "Permission"
|
||||||
},
|
},
|
||||||
"managerPermissions": {
|
|
||||||
"message": "Manager Permissions"
|
|
||||||
},
|
|
||||||
"adminPermissions": {
|
|
||||||
"message": "Admin Permissions"
|
|
||||||
},
|
|
||||||
"accessEventLogs": {
|
"accessEventLogs": {
|
||||||
"message": "Access event logs"
|
"message": "Access event logs"
|
||||||
},
|
},
|
||||||
@@ -4606,9 +4594,6 @@
|
|||||||
"deleteAnyCollection": {
|
"deleteAnyCollection": {
|
||||||
"message": "Delete any collection"
|
"message": "Delete any collection"
|
||||||
},
|
},
|
||||||
"manageAssignedCollections": {
|
|
||||||
"message": "Manage assigned collections"
|
|
||||||
},
|
|
||||||
"editAssignedCollections": {
|
"editAssignedCollections": {
|
||||||
"message": "Edit assigned collections"
|
"message": "Edit assigned collections"
|
||||||
},
|
},
|
||||||
@@ -6669,12 +6654,6 @@
|
|||||||
"restrictedCollectionAssignmentDesc": {
|
"restrictedCollectionAssignmentDesc": {
|
||||||
"message": "You can only assign collections you manage."
|
"message": "You can only assign collections you manage."
|
||||||
},
|
},
|
||||||
"accessAllCollectionsDesc": {
|
|
||||||
"message": "Grant access to all current and future collections."
|
|
||||||
},
|
|
||||||
"accessAllCollectionsHelp": {
|
|
||||||
"message": "If checked, this will replace all other collection permissions."
|
|
||||||
},
|
|
||||||
"selectMembers": {
|
"selectMembers": {
|
||||||
"message": "Select members"
|
"message": "Select members"
|
||||||
},
|
},
|
||||||
@@ -6717,12 +6696,6 @@
|
|||||||
"group": {
|
"group": {
|
||||||
"message": "Group"
|
"message": "Group"
|
||||||
},
|
},
|
||||||
"groupAccessAll": {
|
|
||||||
"message": "This group can access and modify all items."
|
|
||||||
},
|
|
||||||
"memberAccessAll": {
|
|
||||||
"message": "This member can access and modify all items."
|
|
||||||
},
|
|
||||||
"domainVerification": {
|
"domainVerification": {
|
||||||
"message": "Domain verification"
|
"message": "Domain verification"
|
||||||
},
|
},
|
||||||
@@ -8354,5 +8327,8 @@
|
|||||||
},
|
},
|
||||||
"verified": {
|
"verified": {
|
||||||
"message": "Verified"
|
"message": "Verified"
|
||||||
|
},
|
||||||
|
"viewSecret": {
|
||||||
|
"message": "View secret"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,26 @@
|
|||||||
export class BaseAccessPolicyView {
|
class BaseAccessPolicyView {
|
||||||
id: string;
|
|
||||||
read: boolean;
|
read: boolean;
|
||||||
write: boolean;
|
write: boolean;
|
||||||
creationDate: string;
|
|
||||||
revisionDate: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserProjectAccessPolicyView extends BaseAccessPolicyView {
|
export class UserAccessPolicyView extends BaseAccessPolicyView {
|
||||||
organizationUserId: string;
|
organizationUserId: string;
|
||||||
organizationUserName: string;
|
organizationUserName: string;
|
||||||
grantedProjectId: string;
|
|
||||||
userId: string;
|
|
||||||
currentUser: boolean;
|
currentUser: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserServiceAccountAccessPolicyView extends BaseAccessPolicyView {
|
export class GroupAccessPolicyView extends BaseAccessPolicyView {
|
||||||
organizationUserId: string;
|
|
||||||
organizationUserName: string;
|
|
||||||
grantedServiceAccountId: string;
|
|
||||||
userId: string;
|
|
||||||
currentUser: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GroupProjectAccessPolicyView extends BaseAccessPolicyView {
|
|
||||||
groupId: string;
|
groupId: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
grantedProjectId: string;
|
|
||||||
currentUserInGroup: boolean;
|
currentUserInGroup: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GroupServiceAccountAccessPolicyView extends BaseAccessPolicyView {
|
export class ServiceAccountAccessPolicyView extends BaseAccessPolicyView {
|
||||||
groupId: string;
|
|
||||||
groupName: string;
|
|
||||||
grantedServiceAccountId: string;
|
|
||||||
currentUserInGroup: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ServiceAccountProjectAccessPolicyView extends BaseAccessPolicyView {
|
|
||||||
serviceAccountId: string;
|
serviceAccountId: string;
|
||||||
serviceAccountName: string;
|
serviceAccountName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GrantedProjectAccessPolicyView extends BaseAccessPolicyView {
|
||||||
grantedProjectId: string;
|
grantedProjectId: string;
|
||||||
grantedProjectName: string;
|
grantedProjectName: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { GroupProjectAccessPolicyView, UserProjectAccessPolicyView } from "./access-policy.view";
|
import { GroupAccessPolicyView, UserAccessPolicyView } from "./access-policy.view";
|
||||||
|
|
||||||
export class ProjectPeopleAccessPoliciesView {
|
export class ProjectPeopleAccessPoliciesView {
|
||||||
userAccessPolicies: UserProjectAccessPolicyView[];
|
userAccessPolicies: UserAccessPolicyView[];
|
||||||
groupAccessPolicies: GroupProjectAccessPolicyView[];
|
groupAccessPolicies: GroupAccessPolicyView[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ServiceAccountProjectAccessPolicyView } from "./access-policy.view";
|
import { ServiceAccountAccessPolicyView } from "./access-policy.view";
|
||||||
|
|
||||||
export class ProjectServiceAccountsAccessPoliciesView {
|
export class ProjectServiceAccountsAccessPoliciesView {
|
||||||
serviceAccountAccessPolicies: ServiceAccountProjectAccessPolicyView[];
|
serviceAccountAccessPolicies: ServiceAccountAccessPolicyView[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import {
|
||||||
|
GroupAccessPolicyView,
|
||||||
|
UserAccessPolicyView,
|
||||||
|
ServiceAccountAccessPolicyView,
|
||||||
|
} from "./access-policy.view";
|
||||||
|
|
||||||
|
export class SecretAccessPoliciesView {
|
||||||
|
userAccessPolicies: UserAccessPolicyView[];
|
||||||
|
groupAccessPolicies: GroupAccessPolicyView[];
|
||||||
|
serviceAccountAccessPolicies: ServiceAccountAccessPolicyView[];
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { ServiceAccountProjectAccessPolicyView } from "./access-policy.view";
|
import { GrantedProjectAccessPolicyView } from "./access-policy.view";
|
||||||
|
|
||||||
export class ServiceAccountGrantedPoliciesView {
|
export class ServiceAccountGrantedPoliciesView {
|
||||||
grantedProjectPolicies: ServiceAccountProjectPolicyPermissionDetailsView[];
|
grantedProjectPolicies: GrantedProjectPolicyPermissionDetailsView[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ServiceAccountProjectPolicyPermissionDetailsView {
|
export class GrantedProjectPolicyPermissionDetailsView {
|
||||||
accessPolicy: ServiceAccountProjectAccessPolicyView;
|
accessPolicy: GrantedProjectAccessPolicyView;
|
||||||
hasPermission: boolean;
|
hasPermission: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import {
|
import { GroupAccessPolicyView, UserAccessPolicyView } from "./access-policy.view";
|
||||||
GroupServiceAccountAccessPolicyView,
|
|
||||||
UserServiceAccountAccessPolicyView,
|
|
||||||
} from "./access-policy.view";
|
|
||||||
|
|
||||||
export class ServiceAccountPeopleAccessPoliciesView {
|
export class ServiceAccountPeopleAccessPoliciesView {
|
||||||
userAccessPolicies: UserServiceAccountAccessPolicyView[];
|
userAccessPolicies: UserAccessPolicyView[];
|
||||||
groupAccessPolicies: GroupServiceAccountAccessPolicyView[];
|
groupAccessPolicies: GroupAccessPolicyView[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
(deleteSecretsEvent)="openDeleteSecret($event)"
|
(deleteSecretsEvent)="openDeleteSecret($event)"
|
||||||
(newSecretEvent)="openNewSecretDialog()"
|
(newSecretEvent)="openNewSecretDialog()"
|
||||||
(editSecretEvent)="openEditSecret($event)"
|
(editSecretEvent)="openEditSecret($event)"
|
||||||
|
(viewSecretEvent)="openViewSecret($event)"
|
||||||
(copySecretNameEvent)="copySecretName($event)"
|
(copySecretNameEvent)="copySecretName($event)"
|
||||||
(copySecretValueEvent)="copySecretValue($event)"
|
(copySecretValueEvent)="copySecretValue($event)"
|
||||||
(copySecretUuidEvent)="copySecretUuid($event)"
|
(copySecretUuidEvent)="copySecretUuid($event)"
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ import {
|
|||||||
SecretDialogComponent,
|
SecretDialogComponent,
|
||||||
SecretOperation,
|
SecretOperation,
|
||||||
} from "../secrets/dialog/secret-dialog.component";
|
} from "../secrets/dialog/secret-dialog.component";
|
||||||
|
import {
|
||||||
|
SecretViewDialogComponent,
|
||||||
|
SecretViewDialogParams,
|
||||||
|
} from "../secrets/dialog/secret-view-dialog.component";
|
||||||
import { SecretService } from "../secrets/secret.service";
|
import { SecretService } from "../secrets/secret.service";
|
||||||
import {
|
import {
|
||||||
ServiceAccountDialogComponent,
|
ServiceAccountDialogComponent,
|
||||||
@@ -277,6 +281,15 @@ export class OverviewComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openViewSecret(secretId: string) {
|
||||||
|
this.dialogService.open<unknown, SecretViewDialogParams>(SecretViewDialogComponent, {
|
||||||
|
data: {
|
||||||
|
organizationId: this.organizationId,
|
||||||
|
secretId: secretId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
openDeleteSecret(event: SecretListView[]) {
|
openDeleteSecret(event: SecretListView[]) {
|
||||||
this.dialogService.open<unknown, SecretDeleteOperation>(SecretDeleteDialogComponent, {
|
this.dialogService.open<unknown, SecretDeleteOperation>(SecretDeleteDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { DialogService } from "@bitwarden/components";
|
|||||||
import { AccessPolicySelectorService } from "../../shared/access-policies/access-policy-selector/access-policy-selector.service";
|
import { AccessPolicySelectorService } from "../../shared/access-policies/access-policy-selector/access-policy-selector.service";
|
||||||
import {
|
import {
|
||||||
ApItemValueType,
|
ApItemValueType,
|
||||||
convertToProjectPeopleAccessPoliciesView,
|
convertToPeopleAccessPoliciesView,
|
||||||
} from "../../shared/access-policies/access-policy-selector/models/ap-item-value.type";
|
} from "../../shared/access-policies/access-policy-selector/models/ap-item-value.type";
|
||||||
import {
|
import {
|
||||||
ApItemViewType,
|
ApItemViewType,
|
||||||
@@ -119,10 +119,7 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const projectPeopleView = convertToProjectPeopleAccessPoliciesView(
|
const projectPeopleView = convertToPeopleAccessPoliciesView(formValues);
|
||||||
this.projectId,
|
|
||||||
formValues,
|
|
||||||
);
|
|
||||||
const peoplePoliciesViews = await this.accessPolicyService.putProjectPeopleAccessPolicies(
|
const peoplePoliciesViews = await this.accessPolicyService.putProjectPeopleAccessPolicies(
|
||||||
this.projectId,
|
this.projectId,
|
||||||
projectPeopleView,
|
projectPeopleView,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
(deleteSecretsEvent)="openDeleteSecret($event)"
|
(deleteSecretsEvent)="openDeleteSecret($event)"
|
||||||
(newSecretEvent)="openNewSecretDialog()"
|
(newSecretEvent)="openNewSecretDialog()"
|
||||||
(editSecretEvent)="openEditSecret($event)"
|
(editSecretEvent)="openEditSecret($event)"
|
||||||
|
(viewSecretEvent)="openViewSecret($event)"
|
||||||
(copySecretNameEvent)="copySecretName($event)"
|
(copySecretNameEvent)="copySecretName($event)"
|
||||||
(copySecretValueEvent)="copySecretValue($event)"
|
(copySecretValueEvent)="copySecretValue($event)"
|
||||||
(copySecretUuidEvent)="copySecretUuid($event)"
|
(copySecretUuidEvent)="copySecretUuid($event)"
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ import {
|
|||||||
SecretDialogComponent,
|
SecretDialogComponent,
|
||||||
SecretOperation,
|
SecretOperation,
|
||||||
} from "../../secrets/dialog/secret-dialog.component";
|
} from "../../secrets/dialog/secret-dialog.component";
|
||||||
|
import {
|
||||||
|
SecretViewDialogComponent,
|
||||||
|
SecretViewDialogParams,
|
||||||
|
} from "../../secrets/dialog/secret-view-dialog.component";
|
||||||
import { SecretService } from "../../secrets/secret.service";
|
import { SecretService } from "../../secrets/secret.service";
|
||||||
import { SecretsListComponent } from "../../shared/secrets-list.component";
|
import { SecretsListComponent } from "../../shared/secrets-list.component";
|
||||||
import { ProjectService } from "../project.service";
|
import { ProjectService } from "../project.service";
|
||||||
@@ -88,6 +92,15 @@ export class ProjectSecretsComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openViewSecret(secretId: string) {
|
||||||
|
this.dialogService.open<unknown, SecretViewDialogParams>(SecretViewDialogComponent, {
|
||||||
|
data: {
|
||||||
|
organizationId: this.organizationId,
|
||||||
|
secretId: secretId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
openDeleteSecret(event: SecretListView[]) {
|
openDeleteSecret(event: SecretListView[]) {
|
||||||
this.dialogService.open<unknown, SecretDeleteOperation>(SecretDeleteDialogComponent, {
|
this.dialogService.open<unknown, SecretDeleteOperation>(SecretDeleteDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export class ProjectServiceAccountsComponent implements OnInit, OnDestroy {
|
|||||||
projectId: string,
|
projectId: string,
|
||||||
selectedPolicies: ApItemValueType[],
|
selectedPolicies: ApItemValueType[],
|
||||||
): Promise<ProjectServiceAccountsAccessPoliciesView> {
|
): Promise<ProjectServiceAccountsAccessPoliciesView> {
|
||||||
const view = convertToProjectServiceAccountsAccessPoliciesView(projectId, selectedPolicies);
|
const view = convertToProjectServiceAccountsAccessPoliciesView(selectedPolicies);
|
||||||
return await this.accessPolicyService.putProjectServiceAccountsAccessPolicies(
|
return await this.accessPolicyService.putProjectServiceAccountsAccessPolicies(
|
||||||
organizationId,
|
organizationId,
|
||||||
projectId,
|
projectId,
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<form [formGroup]="formGroup">
|
||||||
|
<bit-dialog
|
||||||
|
dialogSize="large"
|
||||||
|
[loading]="loading"
|
||||||
|
[title]="'viewSecret' | i18n"
|
||||||
|
[subtitle]="formGroup.get('name').value"
|
||||||
|
>
|
||||||
|
<ng-container bitDialogContent class="tw-relative">
|
||||||
|
<div class="tw-flex tw-gap-4 tw-pt-4">
|
||||||
|
<bit-form-field class="tw-w-1/3">
|
||||||
|
<bit-label>{{ "name" | i18n }}</bit-label>
|
||||||
|
<input appAutofocus formControlName="name" bitInput />
|
||||||
|
</bit-form-field>
|
||||||
|
<bit-form-field class="tw-w-full">
|
||||||
|
<bit-label>{{ "value" | i18n }}</bit-label>
|
||||||
|
<textarea bitInput rows="4" formControlName="value"></textarea>
|
||||||
|
</bit-form-field>
|
||||||
|
</div>
|
||||||
|
<bit-form-field>
|
||||||
|
<bit-label>{{ "notes" | i18n }}</bit-label>
|
||||||
|
<textarea bitInput rows="4" formControlName="notes"></textarea>
|
||||||
|
</bit-form-field>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container bitDialogFooter>
|
||||||
|
<button type="button" bitButton buttonType="primary" bitFormButton bitDialogClose>
|
||||||
|
{{ "close" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</bit-dialog>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { DIALOG_DATA } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject, OnInit } from "@angular/core";
|
||||||
|
import { FormControl, FormGroup } from "@angular/forms";
|
||||||
|
|
||||||
|
import { SecretService } from "../secret.service";
|
||||||
|
|
||||||
|
export interface SecretViewDialogParams {
|
||||||
|
organizationId: string;
|
||||||
|
secretId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./secret-view-dialog.component.html",
|
||||||
|
})
|
||||||
|
export class SecretViewDialogComponent implements OnInit {
|
||||||
|
protected loading = true;
|
||||||
|
protected formGroup = new FormGroup({
|
||||||
|
name: new FormControl(""),
|
||||||
|
value: new FormControl(""),
|
||||||
|
notes: new FormControl(""),
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private secretService: SecretService,
|
||||||
|
@Inject(DIALOG_DATA) private params: SecretViewDialogParams,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.loading = true;
|
||||||
|
const secret = await this.secretService.getBySecretId(this.params.secretId);
|
||||||
|
this.formGroup.setValue({
|
||||||
|
name: secret.name,
|
||||||
|
value: secret.value,
|
||||||
|
notes: secret.note,
|
||||||
|
});
|
||||||
|
this.formGroup.disable();
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
(deleteSecretsEvent)="openDeleteSecret($event)"
|
(deleteSecretsEvent)="openDeleteSecret($event)"
|
||||||
(newSecretEvent)="openNewSecretDialog()"
|
(newSecretEvent)="openNewSecretDialog()"
|
||||||
(editSecretEvent)="openEditSecret($event)"
|
(editSecretEvent)="openEditSecret($event)"
|
||||||
|
(viewSecretEvent)="openViewSecret($event)"
|
||||||
(copySecretNameEvent)="copySecretName($event)"
|
(copySecretNameEvent)="copySecretName($event)"
|
||||||
(copySecretValueEvent)="copySecretValue($event)"
|
(copySecretValueEvent)="copySecretValue($event)"
|
||||||
(copySecretUuidEvent)="copySecretUuid($event)"
|
(copySecretUuidEvent)="copySecretUuid($event)"
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ import {
|
|||||||
SecretDialogComponent,
|
SecretDialogComponent,
|
||||||
SecretOperation,
|
SecretOperation,
|
||||||
} from "./dialog/secret-dialog.component";
|
} from "./dialog/secret-dialog.component";
|
||||||
|
import {
|
||||||
|
SecretViewDialogComponent,
|
||||||
|
SecretViewDialogParams,
|
||||||
|
} from "./dialog/secret-view-dialog.component";
|
||||||
import { SecretService } from "./secret.service";
|
import { SecretService } from "./secret.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -77,6 +81,15 @@ export class SecretsComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openViewSecret(secretId: string) {
|
||||||
|
this.dialogService.open<unknown, SecretViewDialogParams>(SecretViewDialogComponent, {
|
||||||
|
data: {
|
||||||
|
organizationId: this.organizationId,
|
||||||
|
secretId: secretId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
openDeleteSecret(event: SecretListView[]) {
|
openDeleteSecret(event: SecretListView[]) {
|
||||||
this.dialogService.open<unknown, SecretDeleteOperation>(SecretDeleteDialogComponent, {
|
this.dialogService.open<unknown, SecretDeleteOperation>(SecretDeleteDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -4,12 +4,18 @@ import { SecretsManagerSharedModule } from "../shared/sm-shared.module";
|
|||||||
|
|
||||||
import { SecretDeleteDialogComponent } from "./dialog/secret-delete.component";
|
import { SecretDeleteDialogComponent } from "./dialog/secret-delete.component";
|
||||||
import { SecretDialogComponent } from "./dialog/secret-dialog.component";
|
import { SecretDialogComponent } from "./dialog/secret-dialog.component";
|
||||||
|
import { SecretViewDialogComponent } from "./dialog/secret-view-dialog.component";
|
||||||
import { SecretsRoutingModule } from "./secrets-routing.module";
|
import { SecretsRoutingModule } from "./secrets-routing.module";
|
||||||
import { SecretsComponent } from "./secrets.component";
|
import { SecretsComponent } from "./secrets.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [SecretsManagerSharedModule, SecretsRoutingModule],
|
imports: [SecretsManagerSharedModule, SecretsRoutingModule],
|
||||||
declarations: [SecretDeleteDialogComponent, SecretDialogComponent, SecretsComponent],
|
declarations: [
|
||||||
|
SecretDeleteDialogComponent,
|
||||||
|
SecretDialogComponent,
|
||||||
|
SecretViewDialogComponent,
|
||||||
|
SecretsComponent,
|
||||||
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
})
|
})
|
||||||
export class SecretsModule {}
|
export class SecretsModule {}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { DialogService } from "@bitwarden/components";
|
|||||||
import { AccessPolicySelectorService } from "../../shared/access-policies/access-policy-selector/access-policy-selector.service";
|
import { AccessPolicySelectorService } from "../../shared/access-policies/access-policy-selector/access-policy-selector.service";
|
||||||
import {
|
import {
|
||||||
ApItemValueType,
|
ApItemValueType,
|
||||||
convertToServiceAccountPeopleAccessPoliciesView,
|
convertToPeopleAccessPoliciesView,
|
||||||
} from "../../shared/access-policies/access-policy-selector/models/ap-item-value.type";
|
} from "../../shared/access-policies/access-policy-selector/models/ap-item-value.type";
|
||||||
import {
|
import {
|
||||||
ApItemViewType,
|
ApItemViewType,
|
||||||
@@ -180,10 +180,7 @@ export class ServiceAccountPeopleComponent implements OnInit, OnDestroy {
|
|||||||
serviceAccountId: string,
|
serviceAccountId: string,
|
||||||
selectedPolicies: ApItemValueType[],
|
selectedPolicies: ApItemValueType[],
|
||||||
) {
|
) {
|
||||||
const serviceAccountPeopleView = convertToServiceAccountPeopleAccessPoliciesView(
|
const serviceAccountPeopleView = convertToPeopleAccessPoliciesView(selectedPolicies);
|
||||||
serviceAccountId,
|
|
||||||
selectedPolicies,
|
|
||||||
);
|
|
||||||
return await this.accessPolicyService.putServiceAccountPeopleAccessPolicies(
|
return await this.accessPolicyService.putServiceAccountPeopleAccessPolicies(
|
||||||
serviceAccountId,
|
serviceAccountId,
|
||||||
serviceAccountPeopleView,
|
serviceAccountPeopleView,
|
||||||
|
|||||||
@@ -144,10 +144,7 @@ export class ServiceAccountProjectsComponent implements OnInit, OnDestroy {
|
|||||||
serviceAccountId: string,
|
serviceAccountId: string,
|
||||||
selectedPolicies: ApItemValueType[],
|
selectedPolicies: ApItemValueType[],
|
||||||
): Promise<ServiceAccountGrantedPoliciesView> {
|
): Promise<ServiceAccountGrantedPoliciesView> {
|
||||||
const grantedViews = convertToServiceAccountGrantedPoliciesView(
|
const grantedViews = convertToServiceAccountGrantedPoliciesView(selectedPolicies);
|
||||||
serviceAccountId,
|
|
||||||
selectedPolicies,
|
|
||||||
);
|
|
||||||
return await this.accessPolicyService.putServiceAccountGrantedPolicies(
|
return await this.accessPolicyService.putServiceAccountGrantedPolicies(
|
||||||
organizationId,
|
organizationId,
|
||||||
serviceAccountId,
|
serviceAccountId,
|
||||||
|
|||||||
@@ -0,0 +1,238 @@
|
|||||||
|
import {
|
||||||
|
convertToSecretAccessPoliciesView,
|
||||||
|
convertToPeopleAccessPoliciesView,
|
||||||
|
ApItemValueType,
|
||||||
|
convertToProjectServiceAccountsAccessPoliciesView,
|
||||||
|
convertToServiceAccountGrantedPoliciesView,
|
||||||
|
} from "./ap-item-value.type";
|
||||||
|
import { ApItemEnum } from "./enums/ap-item.enum";
|
||||||
|
import { ApPermissionEnum } from "./enums/ap-permission.enum";
|
||||||
|
|
||||||
|
describe("convertToPeopleAccessPoliciesView", () => {
|
||||||
|
it("should convert selected policy values to user and group access policies view", () => {
|
||||||
|
const selectedPolicyValues = [...createUserApItems(), ...createGroupApItems()];
|
||||||
|
|
||||||
|
const result = convertToPeopleAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.userAccessPolicies).toEqual(expectedUserAccessPolicies);
|
||||||
|
expect(result.groupAccessPolicies).toEqual(expectedGroupAccessPolicies);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty user array if no selected users are provided", () => {
|
||||||
|
const selectedPolicyValues = createGroupApItems();
|
||||||
|
|
||||||
|
const result = convertToPeopleAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.userAccessPolicies).toEqual([]);
|
||||||
|
expect(result.groupAccessPolicies).toEqual(expectedGroupAccessPolicies);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty group array if no selected groups are provided", () => {
|
||||||
|
const selectedPolicyValues = createUserApItems();
|
||||||
|
|
||||||
|
const result = convertToPeopleAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.userAccessPolicies).toEqual(expectedUserAccessPolicies);
|
||||||
|
expect(result.groupAccessPolicies).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty arrays if no selected policy values are provided", () => {
|
||||||
|
const selectedPolicyValues: ApItemValueType[] = [];
|
||||||
|
|
||||||
|
const result = convertToPeopleAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.userAccessPolicies).toEqual([]);
|
||||||
|
expect(result.groupAccessPolicies).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("convertToServiceAccountGrantedPoliciesView", () => {
|
||||||
|
it("should convert selected policy values to ServiceAccountGrantedPoliciesView", () => {
|
||||||
|
const selectedPolicyValues = createProjectApItems();
|
||||||
|
|
||||||
|
const result = convertToServiceAccountGrantedPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.grantedProjectPolicies).toHaveLength(2);
|
||||||
|
expect(result.grantedProjectPolicies[0].accessPolicy.grantedProjectId).toBe(
|
||||||
|
selectedPolicyValues[0].id,
|
||||||
|
);
|
||||||
|
expect(result.grantedProjectPolicies[0].accessPolicy.read).toBe(true);
|
||||||
|
expect(result.grantedProjectPolicies[0].accessPolicy.write).toBe(false);
|
||||||
|
|
||||||
|
expect(result.grantedProjectPolicies[1].accessPolicy.grantedProjectId).toBe(
|
||||||
|
selectedPolicyValues[1].id,
|
||||||
|
);
|
||||||
|
expect(result.grantedProjectPolicies[1].accessPolicy.read).toBe(true);
|
||||||
|
expect(result.grantedProjectPolicies[1].accessPolicy.write).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty array if no selected project policies are provided", () => {
|
||||||
|
const selectedPolicyValues: ApItemValueType[] = [];
|
||||||
|
|
||||||
|
const result = convertToServiceAccountGrantedPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.grantedProjectPolicies).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("convertToProjectServiceAccountsAccessPoliciesView", () => {
|
||||||
|
it("should convert selected policy values to ProjectServiceAccountsAccessPoliciesView", () => {
|
||||||
|
const selectedPolicyValues = createServiceAccountApItems();
|
||||||
|
|
||||||
|
const result = convertToProjectServiceAccountsAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.serviceAccountAccessPolicies).toEqual(expectedServiceAccountAccessPolicies);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty array if nothing is selected.", () => {
|
||||||
|
const selectedPolicyValues: ApItemValueType[] = [];
|
||||||
|
|
||||||
|
const result = convertToProjectServiceAccountsAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.serviceAccountAccessPolicies).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("convertToSecretAccessPoliciesView", () => {
|
||||||
|
it("should convert selected policy values to SecretAccessPoliciesView", () => {
|
||||||
|
const selectedPolicyValues = [
|
||||||
|
...createUserApItems(),
|
||||||
|
...createGroupApItems(),
|
||||||
|
...createServiceAccountApItems(),
|
||||||
|
];
|
||||||
|
const result = convertToSecretAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.userAccessPolicies).toEqual(expectedUserAccessPolicies);
|
||||||
|
expect(result.groupAccessPolicies).toEqual(expectedGroupAccessPolicies);
|
||||||
|
expect(result.serviceAccountAccessPolicies).toEqual(expectedServiceAccountAccessPolicies);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty user array if no selected users are provided", () => {
|
||||||
|
const selectedPolicyValues = [...createGroupApItems(), ...createServiceAccountApItems()];
|
||||||
|
|
||||||
|
const result = convertToSecretAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.userAccessPolicies).toEqual([]);
|
||||||
|
expect(result.groupAccessPolicies).toEqual(expectedGroupAccessPolicies);
|
||||||
|
expect(result.serviceAccountAccessPolicies).toEqual(expectedServiceAccountAccessPolicies);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty group array if no selected groups are provided", () => {
|
||||||
|
const selectedPolicyValues = [...createUserApItems(), ...createServiceAccountApItems()];
|
||||||
|
|
||||||
|
const result = convertToSecretAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.userAccessPolicies).toEqual(expectedUserAccessPolicies);
|
||||||
|
expect(result.groupAccessPolicies).toEqual([]);
|
||||||
|
expect(result.serviceAccountAccessPolicies).toEqual(expectedServiceAccountAccessPolicies);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty service account array if no selected service accounts are provided", () => {
|
||||||
|
const selectedPolicyValues = [...createUserApItems(), ...createGroupApItems()];
|
||||||
|
|
||||||
|
const result = convertToSecretAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.userAccessPolicies).toEqual(expectedUserAccessPolicies);
|
||||||
|
expect(result.groupAccessPolicies).toEqual(expectedGroupAccessPolicies);
|
||||||
|
expect(result.serviceAccountAccessPolicies).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty arrays if nothing is selected.", () => {
|
||||||
|
const selectedPolicyValues: ApItemValueType[] = [];
|
||||||
|
|
||||||
|
const result = convertToSecretAccessPoliciesView(selectedPolicyValues);
|
||||||
|
|
||||||
|
expect(result.userAccessPolicies).toEqual([]);
|
||||||
|
expect(result.groupAccessPolicies).toEqual([]);
|
||||||
|
expect(result.serviceAccountAccessPolicies).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function createUserApItems(): ApItemValueType[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
type: ApItemEnum.User,
|
||||||
|
permission: ApPermissionEnum.CanRead,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
type: ApItemEnum.User,
|
||||||
|
permission: ApPermissionEnum.CanReadWrite,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedUserAccessPolicies = [
|
||||||
|
{
|
||||||
|
organizationUserId: "1",
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
organizationUserId: "3",
|
||||||
|
read: true,
|
||||||
|
write: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function createServiceAccountApItems(): ApItemValueType[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
type: ApItemEnum.ServiceAccount,
|
||||||
|
permission: ApPermissionEnum.CanRead,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
type: ApItemEnum.ServiceAccount,
|
||||||
|
permission: ApPermissionEnum.CanReadWrite,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedServiceAccountAccessPolicies = [
|
||||||
|
{
|
||||||
|
serviceAccountId: "1",
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceAccountId: "2",
|
||||||
|
read: true,
|
||||||
|
write: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function createGroupApItems(): ApItemValueType[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
type: ApItemEnum.Group,
|
||||||
|
permission: ApPermissionEnum.CanReadWrite,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedGroupAccessPolicies = [
|
||||||
|
{
|
||||||
|
groupId: "2",
|
||||||
|
read: true,
|
||||||
|
write: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function createProjectApItems(): ApItemValueType[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
type: ApItemEnum.Project,
|
||||||
|
permission: ApPermissionEnum.CanRead,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
type: ApItemEnum.Project,
|
||||||
|
permission: ApPermissionEnum.CanReadWrite,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,17 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
UserProjectAccessPolicyView,
|
UserAccessPolicyView,
|
||||||
GroupProjectAccessPolicyView,
|
GroupAccessPolicyView,
|
||||||
UserServiceAccountAccessPolicyView,
|
ServiceAccountAccessPolicyView,
|
||||||
GroupServiceAccountAccessPolicyView,
|
GrantedProjectAccessPolicyView,
|
||||||
ServiceAccountProjectAccessPolicyView,
|
|
||||||
} from "../../../../models/view/access-policies/access-policy.view";
|
} from "../../../../models/view/access-policies/access-policy.view";
|
||||||
import { ProjectPeopleAccessPoliciesView } from "../../../../models/view/access-policies/project-people-access-policies.view";
|
|
||||||
import { ProjectServiceAccountsAccessPoliciesView } from "../../../../models/view/access-policies/project-service-accounts-access-policies.view";
|
import { ProjectServiceAccountsAccessPoliciesView } from "../../../../models/view/access-policies/project-service-accounts-access-policies.view";
|
||||||
|
import { SecretAccessPoliciesView } from "../../../../models/view/access-policies/secret-access-policies.view";
|
||||||
import {
|
import {
|
||||||
ServiceAccountGrantedPoliciesView,
|
ServiceAccountGrantedPoliciesView,
|
||||||
ServiceAccountProjectPolicyPermissionDetailsView,
|
GrantedProjectPolicyPermissionDetailsView,
|
||||||
} from "../../../../models/view/access-policies/service-account-granted-policies.view";
|
} from "../../../../models/view/access-policies/service-account-granted-policies.view";
|
||||||
import { ServiceAccountPeopleAccessPoliciesView } from "../../../../models/view/access-policies/service-account-people-access-policies.view";
|
|
||||||
|
|
||||||
import { ApItemEnum } from "./enums/ap-item.enum";
|
import { ApItemEnum } from "./enums/ap-item.enum";
|
||||||
import { ApPermissionEnum, ApPermissionEnumUtil } from "./enums/ap-permission.enum";
|
import { ApPermissionEnum, ApPermissionEnumUtil } from "./enums/ap-permission.enum";
|
||||||
@@ -24,67 +22,14 @@ export type ApItemValueType = {
|
|||||||
currentUser?: boolean;
|
currentUser?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function convertToProjectPeopleAccessPoliciesView(
|
export function convertToPeopleAccessPoliciesView(selectedPolicyValues: ApItemValueType[]) {
|
||||||
projectId: string,
|
return {
|
||||||
selectedPolicyValues: ApItemValueType[],
|
userAccessPolicies: convertToUserAccessPolicyViews(selectedPolicyValues),
|
||||||
): ProjectPeopleAccessPoliciesView {
|
groupAccessPolicies: convertToGroupAccessPolicyViews(selectedPolicyValues),
|
||||||
const view = new ProjectPeopleAccessPoliciesView();
|
};
|
||||||
view.userAccessPolicies = selectedPolicyValues
|
|
||||||
.filter((x) => x.type == ApItemEnum.User)
|
|
||||||
.map((filtered) => {
|
|
||||||
const policyView = new UserProjectAccessPolicyView();
|
|
||||||
policyView.grantedProjectId = projectId;
|
|
||||||
policyView.organizationUserId = filtered.id;
|
|
||||||
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
|
||||||
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
|
||||||
return policyView;
|
|
||||||
});
|
|
||||||
|
|
||||||
view.groupAccessPolicies = selectedPolicyValues
|
|
||||||
.filter((x) => x.type == ApItemEnum.Group)
|
|
||||||
.map((filtered) => {
|
|
||||||
const policyView = new GroupProjectAccessPolicyView();
|
|
||||||
policyView.grantedProjectId = projectId;
|
|
||||||
policyView.groupId = filtered.id;
|
|
||||||
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
|
||||||
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
|
||||||
return policyView;
|
|
||||||
});
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function convertToServiceAccountPeopleAccessPoliciesView(
|
|
||||||
serviceAccountId: string,
|
|
||||||
selectedPolicyValues: ApItemValueType[],
|
|
||||||
): ServiceAccountPeopleAccessPoliciesView {
|
|
||||||
const view = new ServiceAccountPeopleAccessPoliciesView();
|
|
||||||
view.userAccessPolicies = selectedPolicyValues
|
|
||||||
.filter((x) => x.type == ApItemEnum.User)
|
|
||||||
.map((filtered) => {
|
|
||||||
const policyView = new UserServiceAccountAccessPolicyView();
|
|
||||||
policyView.grantedServiceAccountId = serviceAccountId;
|
|
||||||
policyView.organizationUserId = filtered.id;
|
|
||||||
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
|
||||||
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
|
||||||
policyView.currentUser = filtered.currentUser;
|
|
||||||
return policyView;
|
|
||||||
});
|
|
||||||
|
|
||||||
view.groupAccessPolicies = selectedPolicyValues
|
|
||||||
.filter((x) => x.type == ApItemEnum.Group)
|
|
||||||
.map((filtered) => {
|
|
||||||
const policyView = new GroupServiceAccountAccessPolicyView();
|
|
||||||
policyView.grantedServiceAccountId = serviceAccountId;
|
|
||||||
policyView.groupId = filtered.id;
|
|
||||||
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
|
||||||
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
|
||||||
return policyView;
|
|
||||||
});
|
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertToServiceAccountGrantedPoliciesView(
|
export function convertToServiceAccountGrantedPoliciesView(
|
||||||
serviceAccountId: string,
|
|
||||||
selectedPolicyValues: ApItemValueType[],
|
selectedPolicyValues: ApItemValueType[],
|
||||||
): ServiceAccountGrantedPoliciesView {
|
): ServiceAccountGrantedPoliciesView {
|
||||||
const view = new ServiceAccountGrantedPoliciesView();
|
const view = new ServiceAccountGrantedPoliciesView();
|
||||||
@@ -92,9 +37,8 @@ export function convertToServiceAccountGrantedPoliciesView(
|
|||||||
view.grantedProjectPolicies = selectedPolicyValues
|
view.grantedProjectPolicies = selectedPolicyValues
|
||||||
.filter((x) => x.type == ApItemEnum.Project)
|
.filter((x) => x.type == ApItemEnum.Project)
|
||||||
.map((filtered) => {
|
.map((filtered) => {
|
||||||
const detailView = new ServiceAccountProjectPolicyPermissionDetailsView();
|
const detailView = new GrantedProjectPolicyPermissionDetailsView();
|
||||||
const policyView = new ServiceAccountProjectAccessPolicyView();
|
const policyView = new GrantedProjectAccessPolicyView();
|
||||||
policyView.serviceAccountId = serviceAccountId;
|
|
||||||
policyView.grantedProjectId = filtered.id;
|
policyView.grantedProjectId = filtered.id;
|
||||||
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
||||||
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
||||||
@@ -107,21 +51,57 @@ export function convertToServiceAccountGrantedPoliciesView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function convertToProjectServiceAccountsAccessPoliciesView(
|
export function convertToProjectServiceAccountsAccessPoliciesView(
|
||||||
projectId: string,
|
|
||||||
selectedPolicyValues: ApItemValueType[],
|
selectedPolicyValues: ApItemValueType[],
|
||||||
): ProjectServiceAccountsAccessPoliciesView {
|
): ProjectServiceAccountsAccessPoliciesView {
|
||||||
const view = new ProjectServiceAccountsAccessPoliciesView();
|
return {
|
||||||
|
serviceAccountAccessPolicies: convertToServiceAccountAccessPolicyViews(selectedPolicyValues),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
view.serviceAccountAccessPolicies = selectedPolicyValues
|
export function convertToSecretAccessPoliciesView(
|
||||||
.filter((x) => x.type == ApItemEnum.ServiceAccount)
|
selectedPolicyValues: ApItemValueType[],
|
||||||
|
): SecretAccessPoliciesView {
|
||||||
|
return {
|
||||||
|
userAccessPolicies: convertToUserAccessPolicyViews(selectedPolicyValues),
|
||||||
|
groupAccessPolicies: convertToGroupAccessPolicyViews(selectedPolicyValues),
|
||||||
|
serviceAccountAccessPolicies: convertToServiceAccountAccessPolicyViews(selectedPolicyValues),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToUserAccessPolicyViews(apItemValues: ApItemValueType[]): UserAccessPolicyView[] {
|
||||||
|
return apItemValues
|
||||||
|
.filter((x) => x.type == ApItemEnum.User)
|
||||||
.map((filtered) => {
|
.map((filtered) => {
|
||||||
const policyView = new ServiceAccountProjectAccessPolicyView();
|
const policyView = new UserAccessPolicyView();
|
||||||
policyView.serviceAccountId = filtered.id;
|
policyView.organizationUserId = filtered.id;
|
||||||
policyView.grantedProjectId = projectId;
|
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
||||||
|
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
||||||
|
return policyView;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToGroupAccessPolicyViews(apItemValues: ApItemValueType[]): GroupAccessPolicyView[] {
|
||||||
|
return apItemValues
|
||||||
|
.filter((x) => x.type == ApItemEnum.Group)
|
||||||
|
.map((filtered) => {
|
||||||
|
const policyView = new GroupAccessPolicyView();
|
||||||
|
policyView.groupId = filtered.id;
|
||||||
|
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
||||||
|
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
||||||
|
return policyView;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToServiceAccountAccessPolicyViews(
|
||||||
|
apItemValues: ApItemValueType[],
|
||||||
|
): ServiceAccountAccessPolicyView[] {
|
||||||
|
return apItemValues
|
||||||
|
.filter((x) => x.type == ApItemEnum.ServiceAccount)
|
||||||
|
.map((filtered) => {
|
||||||
|
const policyView = new ServiceAccountAccessPolicyView();
|
||||||
|
policyView.serviceAccountId = filtered.id;
|
||||||
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
||||||
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
||||||
return policyView;
|
return policyView;
|
||||||
});
|
});
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,310 @@
|
|||||||
|
import {
|
||||||
|
GroupAccessPolicyView,
|
||||||
|
ServiceAccountAccessPolicyView,
|
||||||
|
UserAccessPolicyView,
|
||||||
|
} from "../../../../models/view/access-policies/access-policy.view";
|
||||||
|
import { ProjectPeopleAccessPoliciesView } from "../../../../models/view/access-policies/project-people-access-policies.view";
|
||||||
|
import { ProjectServiceAccountsAccessPoliciesView } from "../../../../models/view/access-policies/project-service-accounts-access-policies.view";
|
||||||
|
import { SecretAccessPoliciesView } from "../../../../models/view/access-policies/secret-access-policies.view";
|
||||||
|
import { ServiceAccountGrantedPoliciesView } from "../../../../models/view/access-policies/service-account-granted-policies.view";
|
||||||
|
import { ServiceAccountPeopleAccessPoliciesView } from "../../../../models/view/access-policies/service-account-people-access-policies.view";
|
||||||
|
|
||||||
|
import {
|
||||||
|
convertGrantedPoliciesToAccessPolicyItemViews,
|
||||||
|
convertProjectServiceAccountsViewToApItemViews,
|
||||||
|
convertSecretAccessPoliciesToApItemViews,
|
||||||
|
convertToAccessPolicyItemViews,
|
||||||
|
} from "./ap-item-view.type";
|
||||||
|
import { ApItemEnum } from "./enums/ap-item.enum";
|
||||||
|
import { ApPermissionEnum } from "./enums/ap-permission.enum";
|
||||||
|
|
||||||
|
describe("convertToAccessPolicyItemViews", () => {
|
||||||
|
it("should convert ProjectPeopleAccessPoliciesView to ApItemViewType array", () => {
|
||||||
|
const accessPoliciesView: ProjectPeopleAccessPoliciesView = createPeopleAccessPoliciesView();
|
||||||
|
|
||||||
|
const result = convertToAccessPolicyItemViews(accessPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual([...expectedUserApItemViews, ...expectedGroupApItemViews]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert empty ProjectPeopleAccessPoliciesView to empty ApItemViewType array", () => {
|
||||||
|
const accessPoliciesView = new ProjectPeopleAccessPoliciesView();
|
||||||
|
accessPoliciesView.userAccessPolicies = [];
|
||||||
|
accessPoliciesView.groupAccessPolicies = [];
|
||||||
|
|
||||||
|
const result = convertToAccessPolicyItemViews(accessPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert ServiceAccountPeopleAccessPoliciesView to ApItemViewType array", () => {
|
||||||
|
const accessPoliciesView: ServiceAccountPeopleAccessPoliciesView =
|
||||||
|
createPeopleAccessPoliciesView();
|
||||||
|
|
||||||
|
const result = convertToAccessPolicyItemViews(accessPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual([...expectedUserApItemViews, ...expectedGroupApItemViews]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert empty ServiceAccountPeopleAccessPoliciesView to empty ApItemViewType array", () => {
|
||||||
|
const accessPoliciesView = new ServiceAccountPeopleAccessPoliciesView();
|
||||||
|
accessPoliciesView.userAccessPolicies = [];
|
||||||
|
accessPoliciesView.groupAccessPolicies = [];
|
||||||
|
|
||||||
|
const result = convertToAccessPolicyItemViews(accessPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("convertGrantedPoliciesToAccessPolicyItemViews", () => {
|
||||||
|
it("should convert ServiceAccountGrantedPoliciesView to ApItemViewType array", () => {
|
||||||
|
const grantedPoliciesView: ServiceAccountGrantedPoliciesView = createGrantedPoliciesView();
|
||||||
|
|
||||||
|
const result = convertGrantedPoliciesToAccessPolicyItemViews(grantedPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual(expectedGrantedProjectApItemViews);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert empty ServiceAccountGrantedPoliciesView to empty ApItemViewType array", () => {
|
||||||
|
const grantedPoliciesView = new ServiceAccountGrantedPoliciesView();
|
||||||
|
grantedPoliciesView.grantedProjectPolicies = [];
|
||||||
|
|
||||||
|
const result = convertGrantedPoliciesToAccessPolicyItemViews(grantedPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("convertProjectServiceAccountsViewToApItemViews", () => {
|
||||||
|
it("should convert ProjectServiceAccountsAccessPoliciesView to ApItemViewType array", () => {
|
||||||
|
const accessPoliciesView = createProjectServiceAccountsAccessPoliciesView();
|
||||||
|
|
||||||
|
const result = convertProjectServiceAccountsViewToApItemViews(accessPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual([...expectedServiceAccountAccessPolicyViews]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert empty ProjectPeopleAccessPoliciesView to empty ApItemViewType array", () => {
|
||||||
|
const accessPoliciesView = new ProjectServiceAccountsAccessPoliciesView();
|
||||||
|
accessPoliciesView.serviceAccountAccessPolicies = [];
|
||||||
|
|
||||||
|
const result = convertProjectServiceAccountsViewToApItemViews(accessPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("convertSecretAccessPoliciesToApItemViews", () => {
|
||||||
|
it("should convert SecretAccessPoliciesView to ApItemViewType array", () => {
|
||||||
|
const accessPoliciesView = createSecretAccessPoliciesView();
|
||||||
|
|
||||||
|
const result = convertSecretAccessPoliciesToApItemViews(accessPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
...expectedUserApItemViews,
|
||||||
|
...expectedGroupApItemViews,
|
||||||
|
...expectedServiceAccountAccessPolicyViews,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should convert empty SecretAccessPoliciesView to empty ApItemViewType array", () => {
|
||||||
|
const accessPoliciesView = new SecretAccessPoliciesView();
|
||||||
|
accessPoliciesView.userAccessPolicies = [];
|
||||||
|
accessPoliciesView.groupAccessPolicies = [];
|
||||||
|
accessPoliciesView.serviceAccountAccessPolicies = [];
|
||||||
|
|
||||||
|
const result = convertSecretAccessPoliciesToApItemViews(accessPoliciesView);
|
||||||
|
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function createUserAccessPolicyViews(): UserAccessPolicyView[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
organizationUserId: "1",
|
||||||
|
organizationUserName: "Example organization user name",
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
currentUser: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
organizationUserId: "2",
|
||||||
|
organizationUserName: "Example organization user name",
|
||||||
|
read: true,
|
||||||
|
write: true,
|
||||||
|
currentUser: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedUserApItemViews = [
|
||||||
|
{
|
||||||
|
type: ApItemEnum.User,
|
||||||
|
icon: "bwi-user",
|
||||||
|
id: "1",
|
||||||
|
labelName: "Example organization user name",
|
||||||
|
listName: "Example organization user name",
|
||||||
|
permission: ApPermissionEnum.CanRead,
|
||||||
|
currentUser: true,
|
||||||
|
readOnly: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ApItemEnum.User,
|
||||||
|
icon: "bwi-user",
|
||||||
|
id: "2",
|
||||||
|
labelName: "Example organization user name",
|
||||||
|
listName: "Example organization user name",
|
||||||
|
permission: ApPermissionEnum.CanReadWrite,
|
||||||
|
currentUser: false,
|
||||||
|
readOnly: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function createGroupAccessPolicyViews(): GroupAccessPolicyView[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
groupId: "3",
|
||||||
|
groupName: "Example group name",
|
||||||
|
currentUserInGroup: true,
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
groupId: "4",
|
||||||
|
groupName: "Example group name",
|
||||||
|
currentUserInGroup: false,
|
||||||
|
read: true,
|
||||||
|
write: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedGroupApItemViews = [
|
||||||
|
{
|
||||||
|
type: ApItemEnum.Group,
|
||||||
|
icon: "bwi-family",
|
||||||
|
id: "3",
|
||||||
|
labelName: "Example group name",
|
||||||
|
listName: "Example group name",
|
||||||
|
permission: ApPermissionEnum.CanRead,
|
||||||
|
currentUserInGroup: true,
|
||||||
|
readOnly: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ApItemEnum.Group,
|
||||||
|
icon: "bwi-family",
|
||||||
|
id: "4",
|
||||||
|
labelName: "Example group name",
|
||||||
|
listName: "Example group name",
|
||||||
|
permission: ApPermissionEnum.CanReadWrite,
|
||||||
|
currentUserInGroup: false,
|
||||||
|
readOnly: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function createServiceAccountAccessPolicyViews(): ServiceAccountAccessPolicyView[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
serviceAccountId: "5",
|
||||||
|
serviceAccountName: "service account name",
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceAccountId: "6",
|
||||||
|
serviceAccountName: "service account name",
|
||||||
|
read: true,
|
||||||
|
write: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedServiceAccountAccessPolicyViews = [
|
||||||
|
{
|
||||||
|
type: ApItemEnum.ServiceAccount,
|
||||||
|
icon: "bwi-wrench",
|
||||||
|
id: "5",
|
||||||
|
labelName: "service account name",
|
||||||
|
listName: "service account name",
|
||||||
|
permission: ApPermissionEnum.CanRead,
|
||||||
|
readOnly: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ApItemEnum.ServiceAccount,
|
||||||
|
icon: "bwi-wrench",
|
||||||
|
id: "6",
|
||||||
|
labelName: "service account name",
|
||||||
|
listName: "service account name",
|
||||||
|
permission: ApPermissionEnum.CanReadWrite,
|
||||||
|
readOnly: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function createGrantedPoliciesView() {
|
||||||
|
return {
|
||||||
|
grantedProjectPolicies: [
|
||||||
|
{
|
||||||
|
accessPolicy: {
|
||||||
|
grantedProjectId: "1",
|
||||||
|
grantedProjectName: "Example project name",
|
||||||
|
read: true,
|
||||||
|
write: false,
|
||||||
|
},
|
||||||
|
hasPermission: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessPolicy: {
|
||||||
|
grantedProjectId: "2",
|
||||||
|
grantedProjectName: "project name",
|
||||||
|
read: true,
|
||||||
|
write: true,
|
||||||
|
},
|
||||||
|
hasPermission: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedGrantedProjectApItemViews = [
|
||||||
|
{
|
||||||
|
type: ApItemEnum.Project,
|
||||||
|
icon: "bwi-collection",
|
||||||
|
id: "1",
|
||||||
|
labelName: "Example project name",
|
||||||
|
listName: "Example project name",
|
||||||
|
permission: ApPermissionEnum.CanRead,
|
||||||
|
readOnly: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ApItemEnum.Project,
|
||||||
|
icon: "bwi-collection",
|
||||||
|
id: "2",
|
||||||
|
labelName: "project name",
|
||||||
|
listName: "project name",
|
||||||
|
permission: ApPermissionEnum.CanReadWrite,
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function createPeopleAccessPoliciesView() {
|
||||||
|
return {
|
||||||
|
userAccessPolicies: createUserAccessPolicyViews(),
|
||||||
|
groupAccessPolicies: createGroupAccessPolicyViews(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProjectServiceAccountsAccessPoliciesView(): ProjectServiceAccountsAccessPoliciesView {
|
||||||
|
return {
|
||||||
|
serviceAccountAccessPolicies: createServiceAccountAccessPolicyViews(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSecretAccessPoliciesView(): SecretAccessPoliciesView {
|
||||||
|
return {
|
||||||
|
userAccessPolicies: createUserAccessPolicyViews(),
|
||||||
|
groupAccessPolicies: createGroupAccessPolicyViews(),
|
||||||
|
serviceAccountAccessPolicies: createServiceAccountAccessPolicyViews(),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { SelectItemView } from "@bitwarden/components";
|
import { SelectItemView } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import {
|
||||||
|
GroupAccessPolicyView,
|
||||||
|
ServiceAccountAccessPolicyView,
|
||||||
|
UserAccessPolicyView,
|
||||||
|
} from "../../../../models/view/access-policies/access-policy.view";
|
||||||
import { PotentialGranteeView } from "../../../../models/view/access-policies/potential-grantee.view";
|
import { PotentialGranteeView } from "../../../../models/view/access-policies/potential-grantee.view";
|
||||||
import { ProjectPeopleAccessPoliciesView } from "../../../../models/view/access-policies/project-people-access-policies.view";
|
import { ProjectPeopleAccessPoliciesView } from "../../../../models/view/access-policies/project-people-access-policies.view";
|
||||||
import { ProjectServiceAccountsAccessPoliciesView } from "../../../../models/view/access-policies/project-service-accounts-access-policies.view";
|
import { ProjectServiceAccountsAccessPoliciesView } from "../../../../models/view/access-policies/project-service-accounts-access-policies.view";
|
||||||
|
import { SecretAccessPoliciesView } from "../../../../models/view/access-policies/secret-access-policies.view";
|
||||||
import { ServiceAccountGrantedPoliciesView } from "../../../../models/view/access-policies/service-account-granted-policies.view";
|
import { ServiceAccountGrantedPoliciesView } from "../../../../models/view/access-policies/service-account-granted-policies.view";
|
||||||
import { ServiceAccountPeopleAccessPoliciesView } from "../../../../models/view/access-policies/service-account-people-access-policies.view";
|
import { ServiceAccountPeopleAccessPoliciesView } from "../../../../models/view/access-policies/service-account-people-access-policies.view";
|
||||||
|
|
||||||
@@ -11,7 +17,6 @@ import { ApItemEnum, ApItemEnumUtil } from "./enums/ap-item.enum";
|
|||||||
import { ApPermissionEnum, ApPermissionEnumUtil } from "./enums/ap-permission.enum";
|
import { ApPermissionEnum, ApPermissionEnumUtil } from "./enums/ap-permission.enum";
|
||||||
|
|
||||||
export type ApItemViewType = SelectItemView & {
|
export type ApItemViewType = SelectItemView & {
|
||||||
accessPolicyId?: string;
|
|
||||||
permission?: ApPermissionEnum;
|
permission?: ApPermissionEnum;
|
||||||
/**
|
/**
|
||||||
* Flag that this item cannot be modified.
|
* Flag that this item cannot be modified.
|
||||||
@@ -22,7 +27,6 @@ export type ApItemViewType = SelectItemView & {
|
|||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
type: ApItemEnum.User;
|
type: ApItemEnum.User;
|
||||||
userId?: string;
|
|
||||||
currentUser?: boolean;
|
currentUser?: boolean;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
@@ -40,38 +44,10 @@ export type ApItemViewType = SelectItemView & {
|
|||||||
export function convertToAccessPolicyItemViews(
|
export function convertToAccessPolicyItemViews(
|
||||||
value: ProjectPeopleAccessPoliciesView | ServiceAccountPeopleAccessPoliciesView,
|
value: ProjectPeopleAccessPoliciesView | ServiceAccountPeopleAccessPoliciesView,
|
||||||
): ApItemViewType[] {
|
): ApItemViewType[] {
|
||||||
const accessPolicies: ApItemViewType[] = [];
|
return [
|
||||||
|
...toUserApItemViews(value.userAccessPolicies),
|
||||||
value.userAccessPolicies.forEach((policy) => {
|
...toGroupApItemViews(value.groupAccessPolicies),
|
||||||
accessPolicies.push({
|
];
|
||||||
type: ApItemEnum.User,
|
|
||||||
icon: ApItemEnumUtil.itemIcon(ApItemEnum.User),
|
|
||||||
id: policy.organizationUserId,
|
|
||||||
accessPolicyId: policy.id,
|
|
||||||
labelName: policy.organizationUserName,
|
|
||||||
listName: policy.organizationUserName,
|
|
||||||
permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write),
|
|
||||||
userId: policy.userId,
|
|
||||||
currentUser: policy.currentUser,
|
|
||||||
readOnly: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
value.groupAccessPolicies.forEach((policy) => {
|
|
||||||
accessPolicies.push({
|
|
||||||
type: ApItemEnum.Group,
|
|
||||||
icon: ApItemEnumUtil.itemIcon(ApItemEnum.Group),
|
|
||||||
id: policy.groupId,
|
|
||||||
accessPolicyId: policy.id,
|
|
||||||
labelName: policy.groupName,
|
|
||||||
listName: policy.groupName,
|
|
||||||
permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write),
|
|
||||||
currentUserInGroup: policy.currentUserInGroup,
|
|
||||||
readOnly: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return accessPolicies;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertGrantedPoliciesToAccessPolicyItemViews(
|
export function convertGrantedPoliciesToAccessPolicyItemViews(
|
||||||
@@ -84,7 +60,6 @@ export function convertGrantedPoliciesToAccessPolicyItemViews(
|
|||||||
type: ApItemEnum.Project,
|
type: ApItemEnum.Project,
|
||||||
icon: ApItemEnumUtil.itemIcon(ApItemEnum.Project),
|
icon: ApItemEnumUtil.itemIcon(ApItemEnum.Project),
|
||||||
id: detailView.accessPolicy.grantedProjectId,
|
id: detailView.accessPolicy.grantedProjectId,
|
||||||
accessPolicyId: detailView.accessPolicy.id,
|
|
||||||
labelName: detailView.accessPolicy.grantedProjectName,
|
labelName: detailView.accessPolicy.grantedProjectName,
|
||||||
listName: detailView.accessPolicy.grantedProjectName,
|
listName: detailView.accessPolicy.grantedProjectName,
|
||||||
permission: ApPermissionEnumUtil.toApPermissionEnum(
|
permission: ApPermissionEnumUtil.toApPermissionEnum(
|
||||||
@@ -100,24 +75,17 @@ export function convertGrantedPoliciesToAccessPolicyItemViews(
|
|||||||
export function convertProjectServiceAccountsViewToApItemViews(
|
export function convertProjectServiceAccountsViewToApItemViews(
|
||||||
value: ProjectServiceAccountsAccessPoliciesView,
|
value: ProjectServiceAccountsAccessPoliciesView,
|
||||||
): ApItemViewType[] {
|
): ApItemViewType[] {
|
||||||
const accessPolicies: ApItemViewType[] = [];
|
return toServiceAccountsApItemViews(value.serviceAccountAccessPolicies);
|
||||||
|
}
|
||||||
|
|
||||||
value.serviceAccountAccessPolicies.forEach((accessPolicyView) => {
|
export function convertSecretAccessPoliciesToApItemViews(
|
||||||
accessPolicies.push({
|
value: SecretAccessPoliciesView,
|
||||||
type: ApItemEnum.ServiceAccount,
|
): ApItemViewType[] {
|
||||||
icon: ApItemEnumUtil.itemIcon(ApItemEnum.ServiceAccount),
|
return [
|
||||||
id: accessPolicyView.serviceAccountId,
|
...toUserApItemViews(value.userAccessPolicies),
|
||||||
accessPolicyId: accessPolicyView.id,
|
...toGroupApItemViews(value.groupAccessPolicies),
|
||||||
labelName: accessPolicyView.serviceAccountName,
|
...toServiceAccountsApItemViews(value.serviceAccountAccessPolicies),
|
||||||
listName: accessPolicyView.serviceAccountName,
|
];
|
||||||
permission: ApPermissionEnumUtil.toApPermissionEnum(
|
|
||||||
accessPolicyView.read,
|
|
||||||
accessPolicyView.write,
|
|
||||||
),
|
|
||||||
readOnly: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return accessPolicies;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertPotentialGranteesToApItemViewType(
|
export function convertPotentialGranteesToApItemViewType(
|
||||||
@@ -166,3 +134,49 @@ export function convertPotentialGranteesToApItemViewType(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toUserApItemViews(policies: UserAccessPolicyView[]): ApItemViewType[] {
|
||||||
|
return policies.map((policy) => {
|
||||||
|
return {
|
||||||
|
type: ApItemEnum.User,
|
||||||
|
icon: ApItemEnumUtil.itemIcon(ApItemEnum.User),
|
||||||
|
id: policy.organizationUserId,
|
||||||
|
labelName: policy.organizationUserName,
|
||||||
|
listName: policy.organizationUserName,
|
||||||
|
permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write),
|
||||||
|
currentUser: policy.currentUser,
|
||||||
|
readOnly: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toGroupApItemViews(policies: GroupAccessPolicyView[]): ApItemViewType[] {
|
||||||
|
return policies.map((policy) => {
|
||||||
|
return {
|
||||||
|
type: ApItemEnum.Group,
|
||||||
|
icon: ApItemEnumUtil.itemIcon(ApItemEnum.Group),
|
||||||
|
id: policy.groupId,
|
||||||
|
labelName: policy.groupName,
|
||||||
|
listName: policy.groupName,
|
||||||
|
permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write),
|
||||||
|
currentUserInGroup: policy.currentUserInGroup,
|
||||||
|
readOnly: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toServiceAccountsApItemViews(
|
||||||
|
policies: ServiceAccountAccessPolicyView[],
|
||||||
|
): ApItemViewType[] {
|
||||||
|
return policies.map((policy) => {
|
||||||
|
return {
|
||||||
|
type: ApItemEnum.ServiceAccount,
|
||||||
|
icon: ApItemEnumUtil.itemIcon(ApItemEnum.ServiceAccount),
|
||||||
|
id: policy.serviceAccountId,
|
||||||
|
labelName: policy.serviceAccountName,
|
||||||
|
listName: policy.serviceAccountName,
|
||||||
|
permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write),
|
||||||
|
readOnly: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,18 +8,18 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
|||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
UserProjectAccessPolicyView,
|
UserAccessPolicyView,
|
||||||
GroupProjectAccessPolicyView,
|
GroupAccessPolicyView,
|
||||||
UserServiceAccountAccessPolicyView,
|
ServiceAccountAccessPolicyView,
|
||||||
GroupServiceAccountAccessPolicyView,
|
GrantedProjectAccessPolicyView,
|
||||||
ServiceAccountProjectAccessPolicyView,
|
|
||||||
} from "../../models/view/access-policies/access-policy.view";
|
} from "../../models/view/access-policies/access-policy.view";
|
||||||
import { PotentialGranteeView } from "../../models/view/access-policies/potential-grantee.view";
|
import { PotentialGranteeView } from "../../models/view/access-policies/potential-grantee.view";
|
||||||
import { ProjectPeopleAccessPoliciesView } from "../../models/view/access-policies/project-people-access-policies.view";
|
import { ProjectPeopleAccessPoliciesView } from "../../models/view/access-policies/project-people-access-policies.view";
|
||||||
import { ProjectServiceAccountsAccessPoliciesView } from "../../models/view/access-policies/project-service-accounts-access-policies.view";
|
import { ProjectServiceAccountsAccessPoliciesView } from "../../models/view/access-policies/project-service-accounts-access-policies.view";
|
||||||
|
import { SecretAccessPoliciesView } from "../../models/view/access-policies/secret-access-policies.view";
|
||||||
import {
|
import {
|
||||||
ServiceAccountGrantedPoliciesView,
|
ServiceAccountGrantedPoliciesView,
|
||||||
ServiceAccountProjectPolicyPermissionDetailsView,
|
GrantedProjectPolicyPermissionDetailsView,
|
||||||
} from "../../models/view/access-policies/service-account-granted-policies.view";
|
} from "../../models/view/access-policies/service-account-granted-policies.view";
|
||||||
import { ServiceAccountPeopleAccessPoliciesView } from "../../models/view/access-policies/service-account-people-access-policies.view";
|
import { ServiceAccountPeopleAccessPoliciesView } from "../../models/view/access-policies/service-account-people-access-policies.view";
|
||||||
import { PeopleAccessPoliciesRequest } from "../../shared/access-policies/models/requests/people-access-policies.request";
|
import { PeopleAccessPoliciesRequest } from "../../shared/access-policies/models/requests/people-access-policies.request";
|
||||||
@@ -28,18 +28,18 @@ import { ServiceAccountGrantedPoliciesRequest } from "../access-policies/models/
|
|||||||
import { AccessPolicyRequest } from "./models/requests/access-policy.request";
|
import { AccessPolicyRequest } from "./models/requests/access-policy.request";
|
||||||
import { ProjectServiceAccountsAccessPoliciesRequest } from "./models/requests/project-service-accounts-access-policies.request";
|
import { ProjectServiceAccountsAccessPoliciesRequest } from "./models/requests/project-service-accounts-access-policies.request";
|
||||||
import {
|
import {
|
||||||
GroupServiceAccountAccessPolicyResponse,
|
GroupAccessPolicyResponse,
|
||||||
UserServiceAccountAccessPolicyResponse,
|
UserAccessPolicyResponse,
|
||||||
GroupProjectAccessPolicyResponse,
|
ServiceAccountAccessPolicyResponse,
|
||||||
ServiceAccountProjectAccessPolicyResponse,
|
GrantedProjectAccessPolicyResponse,
|
||||||
UserProjectAccessPolicyResponse,
|
|
||||||
} from "./models/responses/access-policy.response";
|
} from "./models/responses/access-policy.response";
|
||||||
import { PotentialGranteeResponse } from "./models/responses/potential-grantee.response";
|
import { PotentialGranteeResponse } from "./models/responses/potential-grantee.response";
|
||||||
import { ProjectPeopleAccessPoliciesResponse } from "./models/responses/project-people-access-policies.response";
|
import { ProjectPeopleAccessPoliciesResponse } from "./models/responses/project-people-access-policies.response";
|
||||||
import { ProjectServiceAccountsAccessPoliciesResponse } from "./models/responses/project-service-accounts-access-policies.response";
|
import { ProjectServiceAccountsAccessPoliciesResponse } from "./models/responses/project-service-accounts-access-policies.response";
|
||||||
|
import { SecretAccessPoliciesResponse } from "./models/responses/secret-access-policies.response";
|
||||||
import { ServiceAccountGrantedPoliciesPermissionDetailsResponse } from "./models/responses/service-account-granted-policies-permission-details.response";
|
import { ServiceAccountGrantedPoliciesPermissionDetailsResponse } from "./models/responses/service-account-granted-policies-permission-details.response";
|
||||||
import { ServiceAccountPeopleAccessPoliciesResponse } from "./models/responses/service-account-people-access-policies.response";
|
import { ServiceAccountPeopleAccessPoliciesResponse } from "./models/responses/service-account-people-access-policies.response";
|
||||||
import { ServiceAccountProjectPolicyPermissionDetailsResponse } from "./models/responses/service-account-project-policy-permission-details.response";
|
import { GrantedProjectAccessPolicyPermissionDetailsResponse } from "./models/responses/service-account-project-policy-permission-details.response";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: "root",
|
providedIn: "root",
|
||||||
@@ -63,7 +63,7 @@ export class AccessPolicyService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const results = new ProjectPeopleAccessPoliciesResponse(r);
|
const results = new ProjectPeopleAccessPoliciesResponse(r);
|
||||||
return this.createProjectPeopleAccessPoliciesView(results);
|
return this.createPeopleAccessPoliciesView(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
async putProjectPeopleAccessPolicies(
|
async putProjectPeopleAccessPolicies(
|
||||||
@@ -79,7 +79,7 @@ export class AccessPolicyService {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
const results = new ProjectPeopleAccessPoliciesResponse(r);
|
const results = new ProjectPeopleAccessPoliciesResponse(r);
|
||||||
return this.createProjectPeopleAccessPoliciesView(results);
|
return this.createPeopleAccessPoliciesView(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getServiceAccountPeopleAccessPolicies(
|
async getServiceAccountPeopleAccessPolicies(
|
||||||
@@ -94,7 +94,7 @@ export class AccessPolicyService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const results = new ServiceAccountPeopleAccessPoliciesResponse(r);
|
const results = new ServiceAccountPeopleAccessPoliciesResponse(r);
|
||||||
return this.createServiceAccountPeopleAccessPoliciesView(results);
|
return this.createPeopleAccessPoliciesView(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
async putServiceAccountPeopleAccessPolicies(
|
async putServiceAccountPeopleAccessPolicies(
|
||||||
@@ -110,7 +110,7 @@ export class AccessPolicyService {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
const results = new ServiceAccountPeopleAccessPoliciesResponse(r);
|
const results = new ServiceAccountPeopleAccessPoliciesResponse(r);
|
||||||
return this.createServiceAccountPeopleAccessPoliciesView(results);
|
return this.createPeopleAccessPoliciesView(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getServiceAccountGrantedPolicies(
|
async getServiceAccountGrantedPolicies(
|
||||||
@@ -181,6 +181,22 @@ export class AccessPolicyService {
|
|||||||
return await this.createProjectServiceAccountsAccessPoliciesView(result, organizationId);
|
return await this.createProjectServiceAccountsAccessPoliciesView(result, organizationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSecretAccessPolicies(
|
||||||
|
organizationId: string,
|
||||||
|
secretId: string,
|
||||||
|
): Promise<SecretAccessPoliciesView> {
|
||||||
|
const r = await this.apiService.send(
|
||||||
|
"GET",
|
||||||
|
"/secrets/" + secretId + "/access-policies",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = new SecretAccessPoliciesResponse(r);
|
||||||
|
return await this.createSecretAccessPoliciesView(result, organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
async getPeoplePotentialGrantees(organizationId: string) {
|
async getPeoplePotentialGrantees(organizationId: string) {
|
||||||
const r = await this.apiService.send(
|
const r = await this.apiService.send(
|
||||||
"GET",
|
"GET",
|
||||||
@@ -223,12 +239,7 @@ export class AccessPolicyService {
|
|||||||
|
|
||||||
private getAccessPolicyRequest(
|
private getAccessPolicyRequest(
|
||||||
granteeId: string,
|
granteeId: string,
|
||||||
view:
|
view: UserAccessPolicyView | GroupAccessPolicyView | ServiceAccountAccessPolicyView,
|
||||||
| UserProjectAccessPolicyView
|
|
||||||
| UserServiceAccountAccessPolicyView
|
|
||||||
| GroupProjectAccessPolicyView
|
|
||||||
| GroupServiceAccountAccessPolicyView
|
|
||||||
| ServiceAccountProjectAccessPolicyView,
|
|
||||||
) {
|
) {
|
||||||
const request = new AccessPolicyRequest();
|
const request = new AccessPolicyRequest();
|
||||||
request.granteeId = granteeId;
|
request.granteeId = granteeId;
|
||||||
@@ -285,21 +296,79 @@ export class AccessPolicyService {
|
|||||||
|
|
||||||
private createBaseAccessPolicyView(
|
private createBaseAccessPolicyView(
|
||||||
response:
|
response:
|
||||||
| UserProjectAccessPolicyResponse
|
| UserAccessPolicyResponse
|
||||||
| UserServiceAccountAccessPolicyResponse
|
| GroupAccessPolicyResponse
|
||||||
| GroupProjectAccessPolicyResponse
|
| ServiceAccountAccessPolicyResponse
|
||||||
| GroupServiceAccountAccessPolicyResponse
|
| GrantedProjectAccessPolicyResponse,
|
||||||
| ServiceAccountProjectAccessPolicyResponse,
|
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
id: response.id,
|
|
||||||
read: response.read,
|
read: response.read,
|
||||||
write: response.write,
|
write: response.write,
|
||||||
creationDate: response.creationDate,
|
|
||||||
revisionDate: response.revisionDate,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async createGrantedProjectAccessPolicyView(
|
||||||
|
organizationKey: SymmetricCryptoKey,
|
||||||
|
response: GrantedProjectAccessPolicyResponse,
|
||||||
|
): Promise<GrantedProjectAccessPolicyView> {
|
||||||
|
return {
|
||||||
|
...this.createBaseAccessPolicyView(response),
|
||||||
|
grantedProjectId: response.grantedProjectId,
|
||||||
|
grantedProjectName: response.grantedProjectName
|
||||||
|
? await this.encryptService.decryptToUtf8(
|
||||||
|
new EncString(response.grantedProjectName),
|
||||||
|
organizationKey,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private createUserAccessPolicyViews(
|
||||||
|
responses: UserAccessPolicyResponse[],
|
||||||
|
): UserAccessPolicyView[] {
|
||||||
|
return responses.map((response) => {
|
||||||
|
return {
|
||||||
|
...this.createBaseAccessPolicyView(response),
|
||||||
|
organizationUserId: response.organizationUserId,
|
||||||
|
organizationUserName: response.organizationUserName,
|
||||||
|
currentUser: response.currentUser,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private createGroupAccessPolicyViews(
|
||||||
|
responses: GroupAccessPolicyResponse[],
|
||||||
|
): GroupAccessPolicyView[] {
|
||||||
|
return responses.map((response) => {
|
||||||
|
return {
|
||||||
|
...this.createBaseAccessPolicyView(response),
|
||||||
|
groupId: response.groupId,
|
||||||
|
groupName: response.groupName,
|
||||||
|
currentUserInGroup: response.currentUserInGroup,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createServiceAccountAccessPolicyViews(
|
||||||
|
orgKey: SymmetricCryptoKey,
|
||||||
|
responses: ServiceAccountAccessPolicyResponse[],
|
||||||
|
): Promise<ServiceAccountAccessPolicyView[]> {
|
||||||
|
return await Promise.all(
|
||||||
|
responses.map(async (response) => {
|
||||||
|
return {
|
||||||
|
...this.createBaseAccessPolicyView(response),
|
||||||
|
serviceAccountId: response.serviceAccountId,
|
||||||
|
serviceAccountName: response.serviceAccountName
|
||||||
|
? await this.encryptService.decryptToUtf8(
|
||||||
|
new EncString(response.serviceAccountName),
|
||||||
|
orgKey,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private async createPotentialGranteeViews(
|
private async createPotentialGranteeViews(
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
results: PotentialGranteeResponse[],
|
results: PotentialGranteeResponse[],
|
||||||
@@ -332,137 +401,44 @@ export class AccessPolicyService {
|
|||||||
): Promise<ServiceAccountGrantedPoliciesView> {
|
): Promise<ServiceAccountGrantedPoliciesView> {
|
||||||
const orgKey = await this.getOrganizationKey(organizationId);
|
const orgKey = await this.getOrganizationKey(organizationId);
|
||||||
|
|
||||||
const view = new ServiceAccountGrantedPoliciesView();
|
return {
|
||||||
view.grantedProjectPolicies =
|
grantedProjectPolicies: await this.createGrantedProjectPolicyPermissionDetailsViews(
|
||||||
await this.createServiceAccountProjectPolicyPermissionDetailsViews(
|
|
||||||
orgKey,
|
orgKey,
|
||||||
response.grantedProjectPolicies,
|
response.grantedProjectPolicies,
|
||||||
);
|
),
|
||||||
return view;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createServiceAccountProjectPolicyPermissionDetailsViews(
|
private async createGrantedProjectPolicyPermissionDetailsViews(
|
||||||
orgKey: SymmetricCryptoKey,
|
orgKey: SymmetricCryptoKey,
|
||||||
responses: ServiceAccountProjectPolicyPermissionDetailsResponse[],
|
responses: GrantedProjectAccessPolicyPermissionDetailsResponse[],
|
||||||
): Promise<ServiceAccountProjectPolicyPermissionDetailsView[]> {
|
): Promise<GrantedProjectPolicyPermissionDetailsView[]> {
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
responses.map(async (response) => {
|
responses.map(async (response) => {
|
||||||
return await this.createServiceAccountProjectPolicyPermissionDetailsView(orgKey, response);
|
return await this.createGrantedProjectPolicyPermissionDetailsView(orgKey, response);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createServiceAccountProjectPolicyPermissionDetailsView(
|
private async createGrantedProjectPolicyPermissionDetailsView(
|
||||||
orgKey: SymmetricCryptoKey,
|
orgKey: SymmetricCryptoKey,
|
||||||
response: ServiceAccountProjectPolicyPermissionDetailsResponse,
|
response: GrantedProjectAccessPolicyPermissionDetailsResponse,
|
||||||
): Promise<ServiceAccountProjectPolicyPermissionDetailsView> {
|
): Promise<GrantedProjectPolicyPermissionDetailsView> {
|
||||||
const view = new ServiceAccountProjectPolicyPermissionDetailsView();
|
const view = new GrantedProjectPolicyPermissionDetailsView();
|
||||||
view.hasPermission = response.hasPermission;
|
view.hasPermission = response.hasPermission;
|
||||||
view.accessPolicy = await this.createServiceAccountProjectAccessPolicyView(
|
view.accessPolicy = await this.createGrantedProjectAccessPolicyView(
|
||||||
orgKey,
|
orgKey,
|
||||||
response.accessPolicy,
|
response.accessPolicy,
|
||||||
);
|
);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createProjectPeopleAccessPoliciesView(
|
private createPeopleAccessPoliciesView(
|
||||||
peopleAccessPoliciesResponse: ProjectPeopleAccessPoliciesResponse,
|
response: ProjectPeopleAccessPoliciesResponse | ServiceAccountPeopleAccessPoliciesResponse,
|
||||||
): ProjectPeopleAccessPoliciesView {
|
) {
|
||||||
const view = new ProjectPeopleAccessPoliciesView();
|
|
||||||
|
|
||||||
view.userAccessPolicies = peopleAccessPoliciesResponse.userAccessPolicies.map((ap) => {
|
|
||||||
return this.createUserProjectAccessPolicyView(ap);
|
|
||||||
});
|
|
||||||
view.groupAccessPolicies = peopleAccessPoliciesResponse.groupAccessPolicies.map((ap) => {
|
|
||||||
return this.createGroupProjectAccessPolicyView(ap);
|
|
||||||
});
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createServiceAccountPeopleAccessPoliciesView(
|
|
||||||
response: ServiceAccountPeopleAccessPoliciesResponse,
|
|
||||||
): ServiceAccountPeopleAccessPoliciesView {
|
|
||||||
const view = new ServiceAccountPeopleAccessPoliciesView();
|
|
||||||
|
|
||||||
view.userAccessPolicies = response.userAccessPolicies.map((ap) => {
|
|
||||||
return this.createUserServiceAccountAccessPolicyView(ap);
|
|
||||||
});
|
|
||||||
view.groupAccessPolicies = response.groupAccessPolicies.map((ap) => {
|
|
||||||
return this.createGroupServiceAccountAccessPolicyView(ap);
|
|
||||||
});
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createUserProjectAccessPolicyView(
|
|
||||||
response: UserProjectAccessPolicyResponse,
|
|
||||||
): UserProjectAccessPolicyView {
|
|
||||||
return {
|
return {
|
||||||
...this.createBaseAccessPolicyView(response),
|
userAccessPolicies: this.createUserAccessPolicyViews(response.userAccessPolicies),
|
||||||
grantedProjectId: response.grantedProjectId,
|
groupAccessPolicies: this.createGroupAccessPolicyViews(response.groupAccessPolicies),
|
||||||
organizationUserId: response.organizationUserId,
|
|
||||||
organizationUserName: response.organizationUserName,
|
|
||||||
userId: response.userId,
|
|
||||||
currentUser: response.currentUser,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private createGroupProjectAccessPolicyView(
|
|
||||||
response: GroupProjectAccessPolicyResponse,
|
|
||||||
): GroupProjectAccessPolicyView {
|
|
||||||
return {
|
|
||||||
...this.createBaseAccessPolicyView(response),
|
|
||||||
grantedProjectId: response.grantedProjectId,
|
|
||||||
groupId: response.groupId,
|
|
||||||
groupName: response.groupName,
|
|
||||||
currentUserInGroup: response.currentUserInGroup,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createServiceAccountProjectAccessPolicyView(
|
|
||||||
organizationKey: SymmetricCryptoKey,
|
|
||||||
response: ServiceAccountProjectAccessPolicyResponse,
|
|
||||||
): Promise<ServiceAccountProjectAccessPolicyView> {
|
|
||||||
return {
|
|
||||||
...this.createBaseAccessPolicyView(response),
|
|
||||||
grantedProjectId: response.grantedProjectId,
|
|
||||||
serviceAccountId: response.serviceAccountId,
|
|
||||||
grantedProjectName: response.grantedProjectName
|
|
||||||
? await this.encryptService.decryptToUtf8(
|
|
||||||
new EncString(response.grantedProjectName),
|
|
||||||
organizationKey,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
serviceAccountName: response.serviceAccountName
|
|
||||||
? await this.encryptService.decryptToUtf8(
|
|
||||||
new EncString(response.serviceAccountName),
|
|
||||||
organizationKey,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private createUserServiceAccountAccessPolicyView(
|
|
||||||
response: UserServiceAccountAccessPolicyResponse,
|
|
||||||
): UserServiceAccountAccessPolicyView {
|
|
||||||
return {
|
|
||||||
...this.createBaseAccessPolicyView(response),
|
|
||||||
grantedServiceAccountId: response.grantedServiceAccountId,
|
|
||||||
organizationUserId: response.organizationUserId,
|
|
||||||
organizationUserName: response.organizationUserName,
|
|
||||||
userId: response.userId,
|
|
||||||
currentUser: response.currentUser,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private createGroupServiceAccountAccessPolicyView(
|
|
||||||
response: GroupServiceAccountAccessPolicyResponse,
|
|
||||||
): GroupServiceAccountAccessPolicyView {
|
|
||||||
return {
|
|
||||||
...this.createBaseAccessPolicyView(response),
|
|
||||||
grantedServiceAccountId: response.grantedServiceAccountId,
|
|
||||||
groupId: response.groupId,
|
|
||||||
groupName: response.groupName,
|
|
||||||
currentUserInGroup: response.currentUserInGroup,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,13 +447,26 @@ export class AccessPolicyService {
|
|||||||
organizationId: string,
|
organizationId: string,
|
||||||
): Promise<ProjectServiceAccountsAccessPoliciesView> {
|
): Promise<ProjectServiceAccountsAccessPoliciesView> {
|
||||||
const orgKey = await this.getOrganizationKey(organizationId);
|
const orgKey = await this.getOrganizationKey(organizationId);
|
||||||
|
return {
|
||||||
|
serviceAccountAccessPolicies: await this.createServiceAccountAccessPolicyViews(
|
||||||
|
orgKey,
|
||||||
|
response.serviceAccountAccessPolicies,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const view = new ProjectServiceAccountsAccessPoliciesView();
|
private async createSecretAccessPoliciesView(
|
||||||
view.serviceAccountAccessPolicies = await Promise.all(
|
response: SecretAccessPoliciesResponse,
|
||||||
response.serviceAccountAccessPolicies.map(async (ap) => {
|
organizationId: string,
|
||||||
return await this.createServiceAccountProjectAccessPolicyView(orgKey, ap);
|
): Promise<SecretAccessPoliciesView> {
|
||||||
}),
|
const orgKey = await this.getOrganizationKey(organizationId);
|
||||||
);
|
return {
|
||||||
return view;
|
userAccessPolicies: this.createUserAccessPolicyViews(response.userAccessPolicies),
|
||||||
|
groupAccessPolicies: this.createGroupAccessPolicyViews(response.groupAccessPolicies),
|
||||||
|
serviceAccountAccessPolicies: await this.createServiceAccountAccessPolicyViews(
|
||||||
|
orgKey,
|
||||||
|
response.serviceAccountAccessPolicies,
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +1,59 @@
|
|||||||
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
export class BaseAccessPolicyResponse extends BaseResponse {
|
class BaseAccessPolicyResponse extends BaseResponse {
|
||||||
id: string;
|
|
||||||
read: boolean;
|
read: boolean;
|
||||||
write: boolean;
|
write: boolean;
|
||||||
creationDate: string;
|
|
||||||
revisionDate: string;
|
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.id = this.getResponseProperty("Id");
|
|
||||||
this.read = this.getResponseProperty("Read");
|
this.read = this.getResponseProperty("Read");
|
||||||
this.write = this.getResponseProperty("Write");
|
this.write = this.getResponseProperty("Write");
|
||||||
this.creationDate = this.getResponseProperty("CreationDate");
|
|
||||||
this.revisionDate = this.getResponseProperty("RevisionDate");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserProjectAccessPolicyResponse extends BaseAccessPolicyResponse {
|
export class UserAccessPolicyResponse extends BaseAccessPolicyResponse {
|
||||||
organizationUserId: string;
|
organizationUserId: string;
|
||||||
organizationUserName: string;
|
organizationUserName: string;
|
||||||
grantedProjectId: string;
|
|
||||||
userId: string;
|
|
||||||
currentUser: boolean;
|
currentUser: boolean;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.organizationUserId = this.getResponseProperty("OrganizationUserId");
|
this.organizationUserId = this.getResponseProperty("OrganizationUserId");
|
||||||
this.organizationUserName = this.getResponseProperty("OrganizationUserName");
|
this.organizationUserName = this.getResponseProperty("OrganizationUserName");
|
||||||
this.grantedProjectId = this.getResponseProperty("GrantedProjectId");
|
|
||||||
this.userId = this.getResponseProperty("UserId");
|
|
||||||
this.currentUser = this.getResponseProperty("CurrentUser");
|
this.currentUser = this.getResponseProperty("CurrentUser");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserServiceAccountAccessPolicyResponse extends BaseAccessPolicyResponse {
|
export class GroupAccessPolicyResponse extends BaseAccessPolicyResponse {
|
||||||
organizationUserId: string;
|
|
||||||
organizationUserName: string;
|
|
||||||
grantedServiceAccountId: string;
|
|
||||||
userId: string;
|
|
||||||
currentUser: boolean;
|
|
||||||
|
|
||||||
constructor(response: any) {
|
|
||||||
super(response);
|
|
||||||
this.organizationUserId = this.getResponseProperty("OrganizationUserId");
|
|
||||||
this.organizationUserName = this.getResponseProperty("OrganizationUserName");
|
|
||||||
this.grantedServiceAccountId = this.getResponseProperty("GrantedServiceAccountId");
|
|
||||||
this.userId = this.getResponseProperty("UserId");
|
|
||||||
this.currentUser = this.getResponseProperty("CurrentUser");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GroupProjectAccessPolicyResponse extends BaseAccessPolicyResponse {
|
|
||||||
groupId: string;
|
groupId: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
grantedProjectId: string;
|
|
||||||
currentUserInGroup: boolean;
|
currentUserInGroup: boolean;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.groupId = this.getResponseProperty("GroupId");
|
this.groupId = this.getResponseProperty("GroupId");
|
||||||
this.groupName = this.getResponseProperty("GroupName");
|
this.groupName = this.getResponseProperty("GroupName");
|
||||||
this.grantedProjectId = this.getResponseProperty("GrantedProjectId");
|
|
||||||
this.currentUserInGroup = this.getResponseProperty("CurrentUserInGroup");
|
this.currentUserInGroup = this.getResponseProperty("CurrentUserInGroup");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GroupServiceAccountAccessPolicyResponse extends BaseAccessPolicyResponse {
|
export class ServiceAccountAccessPolicyResponse extends BaseAccessPolicyResponse {
|
||||||
groupId: string;
|
|
||||||
groupName: string;
|
|
||||||
grantedServiceAccountId: string;
|
|
||||||
currentUserInGroup: boolean;
|
|
||||||
|
|
||||||
constructor(response: any) {
|
|
||||||
super(response);
|
|
||||||
this.groupId = this.getResponseProperty("GroupId");
|
|
||||||
this.groupName = this.getResponseProperty("GroupName");
|
|
||||||
this.grantedServiceAccountId = this.getResponseProperty("GrantedServiceAccountId");
|
|
||||||
this.currentUserInGroup = this.getResponseProperty("CurrentUserInGroup");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ServiceAccountProjectAccessPolicyResponse extends BaseAccessPolicyResponse {
|
|
||||||
serviceAccountId: string;
|
serviceAccountId: string;
|
||||||
serviceAccountName: string;
|
serviceAccountName: string;
|
||||||
grantedProjectId: string;
|
|
||||||
grantedProjectName: string;
|
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.serviceAccountId = this.getResponseProperty("ServiceAccountId");
|
this.serviceAccountId = this.getResponseProperty("ServiceAccountId");
|
||||||
this.serviceAccountName = this.getResponseProperty("ServiceAccountName");
|
this.serviceAccountName = this.getResponseProperty("ServiceAccountName");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GrantedProjectAccessPolicyResponse extends BaseAccessPolicyResponse {
|
||||||
|
grantedProjectId: string;
|
||||||
|
grantedProjectName: string;
|
||||||
|
|
||||||
|
constructor(response: any) {
|
||||||
|
super(response);
|
||||||
this.grantedProjectId = this.getResponseProperty("GrantedProjectId");
|
this.grantedProjectId = this.getResponseProperty("GrantedProjectId");
|
||||||
this.grantedProjectName = this.getResponseProperty("GrantedProjectName");
|
this.grantedProjectName = this.getResponseProperty("GrantedProjectName");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
import {
|
import { GroupAccessPolicyResponse, UserAccessPolicyResponse } from "./access-policy.response";
|
||||||
GroupProjectAccessPolicyResponse,
|
|
||||||
UserProjectAccessPolicyResponse,
|
|
||||||
} from "./access-policy.response";
|
|
||||||
|
|
||||||
export class ProjectPeopleAccessPoliciesResponse extends BaseResponse {
|
export class ProjectPeopleAccessPoliciesResponse extends BaseResponse {
|
||||||
userAccessPolicies: UserProjectAccessPolicyResponse[];
|
userAccessPolicies: UserAccessPolicyResponse[];
|
||||||
groupAccessPolicies: GroupProjectAccessPolicyResponse[];
|
groupAccessPolicies: GroupAccessPolicyResponse[];
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
const userAccessPolicies = this.getResponseProperty("UserAccessPolicies");
|
const userAccessPolicies = this.getResponseProperty("UserAccessPolicies");
|
||||||
this.userAccessPolicies = userAccessPolicies.map(
|
this.userAccessPolicies = userAccessPolicies.map((k: any) => new UserAccessPolicyResponse(k));
|
||||||
(k: any) => new UserProjectAccessPolicyResponse(k),
|
|
||||||
);
|
|
||||||
const groupAccessPolicies = this.getResponseProperty("GroupAccessPolicies");
|
const groupAccessPolicies = this.getResponseProperty("GroupAccessPolicies");
|
||||||
this.groupAccessPolicies = groupAccessPolicies.map(
|
this.groupAccessPolicies = groupAccessPolicies.map(
|
||||||
(k: any) => new GroupProjectAccessPolicyResponse(k),
|
(k: any) => new GroupAccessPolicyResponse(k),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
import { ServiceAccountProjectAccessPolicyResponse } from "./access-policy.response";
|
import { ServiceAccountAccessPolicyResponse } from "./access-policy.response";
|
||||||
|
|
||||||
export class ProjectServiceAccountsAccessPoliciesResponse extends BaseResponse {
|
export class ProjectServiceAccountsAccessPoliciesResponse extends BaseResponse {
|
||||||
serviceAccountAccessPolicies: ServiceAccountProjectAccessPolicyResponse[];
|
serviceAccountAccessPolicies: ServiceAccountAccessPolicyResponse[];
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
const serviceAccountAccessPolicies = this.getResponseProperty("ServiceAccountAccessPolicies");
|
const serviceAccountAccessPolicies = this.getResponseProperty("ServiceAccountAccessPolicies");
|
||||||
this.serviceAccountAccessPolicies = serviceAccountAccessPolicies.map(
|
this.serviceAccountAccessPolicies = serviceAccountAccessPolicies.map(
|
||||||
(k: any) => new ServiceAccountProjectAccessPolicyResponse(k),
|
(k: any) => new ServiceAccountAccessPolicyResponse(k),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
|
import {
|
||||||
|
GroupAccessPolicyResponse,
|
||||||
|
UserAccessPolicyResponse,
|
||||||
|
ServiceAccountAccessPolicyResponse,
|
||||||
|
} from "./access-policy.response";
|
||||||
|
|
||||||
|
export class SecretAccessPoliciesResponse extends BaseResponse {
|
||||||
|
userAccessPolicies: UserAccessPolicyResponse[];
|
||||||
|
groupAccessPolicies: GroupAccessPolicyResponse[];
|
||||||
|
serviceAccountAccessPolicies: ServiceAccountAccessPolicyResponse[];
|
||||||
|
|
||||||
|
constructor(response: any) {
|
||||||
|
super(response);
|
||||||
|
const userAccessPolicies = this.getResponseProperty("UserAccessPolicies");
|
||||||
|
this.userAccessPolicies = userAccessPolicies.map((k: any) => new UserAccessPolicyResponse(k));
|
||||||
|
const groupAccessPolicies = this.getResponseProperty("GroupAccessPolicies");
|
||||||
|
this.groupAccessPolicies = groupAccessPolicies.map(
|
||||||
|
(k: any) => new GroupAccessPolicyResponse(k),
|
||||||
|
);
|
||||||
|
const serviceAccountAccessPolicies = this.getResponseProperty("ServiceAccountAccessPolicies");
|
||||||
|
this.serviceAccountAccessPolicies = serviceAccountAccessPolicies.map(
|
||||||
|
(k: any) => new ServiceAccountAccessPolicyResponse(k),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
import { ServiceAccountProjectPolicyPermissionDetailsResponse } from "./service-account-project-policy-permission-details.response";
|
import { GrantedProjectAccessPolicyPermissionDetailsResponse } from "./service-account-project-policy-permission-details.response";
|
||||||
|
|
||||||
export class ServiceAccountGrantedPoliciesPermissionDetailsResponse extends BaseResponse {
|
export class ServiceAccountGrantedPoliciesPermissionDetailsResponse extends BaseResponse {
|
||||||
grantedProjectPolicies: ServiceAccountProjectPolicyPermissionDetailsResponse[];
|
grantedProjectPolicies: GrantedProjectAccessPolicyPermissionDetailsResponse[];
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
const grantedProjectPolicies = this.getResponseProperty("GrantedProjectPolicies");
|
const grantedProjectPolicies = this.getResponseProperty("GrantedProjectPolicies");
|
||||||
this.grantedProjectPolicies = grantedProjectPolicies.map(
|
this.grantedProjectPolicies = grantedProjectPolicies.map(
|
||||||
(k: any) => new ServiceAccountProjectPolicyPermissionDetailsResponse(k),
|
(k: any) => new GrantedProjectAccessPolicyPermissionDetailsResponse(k),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
import {
|
import { GroupAccessPolicyResponse, UserAccessPolicyResponse } from "./access-policy.response";
|
||||||
GroupServiceAccountAccessPolicyResponse,
|
|
||||||
UserServiceAccountAccessPolicyResponse,
|
|
||||||
} from "./access-policy.response";
|
|
||||||
|
|
||||||
export class ServiceAccountPeopleAccessPoliciesResponse extends BaseResponse {
|
export class ServiceAccountPeopleAccessPoliciesResponse extends BaseResponse {
|
||||||
userAccessPolicies: UserServiceAccountAccessPolicyResponse[];
|
userAccessPolicies: UserAccessPolicyResponse[];
|
||||||
groupAccessPolicies: GroupServiceAccountAccessPolicyResponse[];
|
groupAccessPolicies: GroupAccessPolicyResponse[];
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
const userAccessPolicies = this.getResponseProperty("UserAccessPolicies");
|
const userAccessPolicies = this.getResponseProperty("UserAccessPolicies");
|
||||||
this.userAccessPolicies = userAccessPolicies.map(
|
this.userAccessPolicies = userAccessPolicies.map((k: any) => new UserAccessPolicyResponse(k));
|
||||||
(k: any) => new UserServiceAccountAccessPolicyResponse(k),
|
|
||||||
);
|
|
||||||
const groupAccessPolicies = this.getResponseProperty("GroupAccessPolicies");
|
const groupAccessPolicies = this.getResponseProperty("GroupAccessPolicies");
|
||||||
this.groupAccessPolicies = groupAccessPolicies.map(
|
this.groupAccessPolicies = groupAccessPolicies.map(
|
||||||
(k: any) => new GroupServiceAccountAccessPolicyResponse(k),
|
(k: any) => new GroupAccessPolicyResponse(k),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
import { ServiceAccountProjectAccessPolicyResponse } from "./access-policy.response";
|
import { GrantedProjectAccessPolicyResponse } from "./access-policy.response";
|
||||||
|
|
||||||
export class ServiceAccountProjectPolicyPermissionDetailsResponse extends BaseResponse {
|
export class GrantedProjectAccessPolicyPermissionDetailsResponse extends BaseResponse {
|
||||||
accessPolicy: ServiceAccountProjectAccessPolicyResponse;
|
accessPolicy: GrantedProjectAccessPolicyResponse;
|
||||||
hasPermission: boolean;
|
hasPermission: boolean;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
<i class="bwi bwi-key tw-text-muted" aria-hidden="true"></i>
|
<i class="bwi bwi-key tw-text-muted" aria-hidden="true"></i>
|
||||||
<div>
|
<div>
|
||||||
<div *ngIf="!trash">
|
<div *ngIf="!trash">
|
||||||
<button type="button" bitLink (click)="editSecretEvent.emit(secret.id)">
|
<button type="button" bitLink (click)="editSecret(secret)">
|
||||||
{{ secret.name }}
|
{{ secret.name }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
bitMenuItem
|
bitMenuItem
|
||||||
(click)="editSecretEvent.emit(secret.id)"
|
(click)="editSecret(secret)"
|
||||||
*ngIf="secret.write && !trash"
|
*ngIf="secret.write && !trash"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-fw bwi-pencil" aria-hidden="true"></i>
|
<i class="bwi bwi-fw bwi-pencil" aria-hidden="true"></i>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export class SecretsListComponent implements OnDestroy {
|
|||||||
@Input() trash: boolean;
|
@Input() trash: boolean;
|
||||||
|
|
||||||
@Output() editSecretEvent = new EventEmitter<string>();
|
@Output() editSecretEvent = new EventEmitter<string>();
|
||||||
|
@Output() viewSecretEvent = new EventEmitter<string>();
|
||||||
@Output() copySecretNameEvent = new EventEmitter<string>();
|
@Output() copySecretNameEvent = new EventEmitter<string>();
|
||||||
@Output() copySecretValueEvent = new EventEmitter<string>();
|
@Output() copySecretValueEvent = new EventEmitter<string>();
|
||||||
@Output() copySecretUuidEvent = new EventEmitter<string>();
|
@Output() copySecretUuidEvent = new EventEmitter<string>();
|
||||||
@@ -116,6 +117,14 @@ export class SecretsListComponent implements OnDestroy {
|
|||||||
return aProjects[0]?.name.localeCompare(bProjects[0].name);
|
return aProjects[0]?.name.localeCompare(bProjects[0].name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected editSecret(secret: SecretListView) {
|
||||||
|
if (secret.write) {
|
||||||
|
this.editSecretEvent.emit(secret.id);
|
||||||
|
} else {
|
||||||
|
this.viewSecretEvent.emit(secret.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Refactor to smart component and remove
|
* TODO: Refactor to smart component and remove
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ export class UserTypePipe implements PipeTransform {
|
|||||||
return this.i18nService.t("admin");
|
return this.i18nService.t("admin");
|
||||||
case OrganizationUserType.User:
|
case OrganizationUserType.User:
|
||||||
return this.i18nService.t("user");
|
return this.i18nService.t("user");
|
||||||
case OrganizationUserType.Manager:
|
|
||||||
return this.i18nService.t("manager");
|
|
||||||
case OrganizationUserType.Custom:
|
case OrganizationUserType.Custom:
|
||||||
return this.i18nService.t("custom");
|
return this.i18nService.t("custom");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Observable, Subject, Subscription, firstValueFrom, throwError, timeout } from "rxjs";
|
import { firstValueFrom, Observable, Subject, Subscription, throwError, timeout } from "rxjs";
|
||||||
|
|
||||||
/** Test class to enable async awaiting of observable emissions */
|
/** Test class to enable async awaiting of observable emissions */
|
||||||
export class ObservableTracker<T> {
|
export class ObservableTracker<T> {
|
||||||
@@ -43,6 +43,9 @@ export class ObservableTracker<T> {
|
|||||||
|
|
||||||
private trackEmissions(observable: Observable<T>): T[] {
|
private trackEmissions(observable: Observable<T>): T[] {
|
||||||
const emissions: T[] = [];
|
const emissions: T[] = [];
|
||||||
|
this.emissionReceived.subscribe((value) => {
|
||||||
|
emissions.push(value);
|
||||||
|
});
|
||||||
this.subscription = observable.subscribe((value) => {
|
this.subscription = observable.subscribe((value) => {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
this.emissionReceived.next(null);
|
this.emissionReceived.next(null);
|
||||||
@@ -64,9 +67,7 @@ export class ObservableTracker<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.emissionReceived.subscribe((value) => {
|
|
||||||
emissions.push(value);
|
|
||||||
});
|
|
||||||
return emissions;
|
return emissions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { SelectionReadOnlyRequest } from "../../../models/request/selection-read
|
|||||||
export class OrganizationUserInviteRequest {
|
export class OrganizationUserInviteRequest {
|
||||||
emails: string[] = [];
|
emails: string[] = [];
|
||||||
type: OrganizationUserType;
|
type: OrganizationUserType;
|
||||||
accessAll: boolean;
|
|
||||||
accessSecretsManager: boolean;
|
accessSecretsManager: boolean;
|
||||||
collections: SelectionReadOnlyRequest[] = [];
|
collections: SelectionReadOnlyRequest[] = [];
|
||||||
groups: string[];
|
groups: string[];
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { SelectionReadOnlyRequest } from "../../../models/request/selection-read
|
|||||||
|
|
||||||
export class OrganizationUserUpdateRequest {
|
export class OrganizationUserUpdateRequest {
|
||||||
type: OrganizationUserType;
|
type: OrganizationUserType;
|
||||||
accessAll: boolean;
|
|
||||||
accessSecretsManager: boolean;
|
accessSecretsManager: boolean;
|
||||||
collections: SelectionReadOnlyRequest[] = [];
|
collections: SelectionReadOnlyRequest[] = [];
|
||||||
groups: string[] = [];
|
groups: string[] = [];
|
||||||
|
|||||||
@@ -10,11 +10,6 @@ export class OrganizationUserResponse extends BaseResponse {
|
|||||||
type: OrganizationUserType;
|
type: OrganizationUserType;
|
||||||
status: OrganizationUserStatusType;
|
status: OrganizationUserStatusType;
|
||||||
externalId: string;
|
externalId: string;
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* To be removed after Flexible Collections.
|
|
||||||
**/
|
|
||||||
accessAll: boolean;
|
|
||||||
accessSecretsManager: boolean;
|
accessSecretsManager: boolean;
|
||||||
permissions: PermissionsApi;
|
permissions: PermissionsApi;
|
||||||
resetPasswordEnrolled: boolean;
|
resetPasswordEnrolled: boolean;
|
||||||
@@ -30,7 +25,6 @@ export class OrganizationUserResponse extends BaseResponse {
|
|||||||
this.status = this.getResponseProperty("Status");
|
this.status = this.getResponseProperty("Status");
|
||||||
this.permissions = new PermissionsApi(this.getResponseProperty("Permissions"));
|
this.permissions = new PermissionsApi(this.getResponseProperty("Permissions"));
|
||||||
this.externalId = this.getResponseProperty("ExternalId");
|
this.externalId = this.getResponseProperty("ExternalId");
|
||||||
this.accessAll = this.getResponseProperty("AccessAll");
|
|
||||||
this.accessSecretsManager = this.getResponseProperty("AccessSecretsManager");
|
this.accessSecretsManager = this.getResponseProperty("AccessSecretsManager");
|
||||||
this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled");
|
this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled");
|
||||||
this.hasMasterPassword = this.getResponseProperty("HasMasterPassword");
|
this.hasMasterPassword = this.getResponseProperty("HasMasterPassword");
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { OrganizationData } from "../../models/data/organization.data";
|
|||||||
import { Organization } from "../../models/domain/organization";
|
import { Organization } from "../../models/domain/organization";
|
||||||
|
|
||||||
export function canAccessVaultTab(org: Organization): boolean {
|
export function canAccessVaultTab(org: Organization): boolean {
|
||||||
return org.canViewAssignedCollections || org.canViewAllCollections;
|
return org.canViewAllCollections;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canAccessSettingsTab(org: Organization): boolean {
|
export function canAccessSettingsTab(org: Organization): boolean {
|
||||||
@@ -77,10 +77,7 @@ export function canAccessImportExport(i18nService: I18nService) {
|
|||||||
export function canAccessImport(i18nService: I18nService) {
|
export function canAccessImport(i18nService: I18nService) {
|
||||||
return map<Organization[], Organization[]>((orgs) =>
|
return map<Organization[], Organization[]>((orgs) =>
|
||||||
orgs
|
orgs
|
||||||
.filter(
|
.filter((org) => org.canAccessImportExport || org.canCreateNewCollections)
|
||||||
(org) =>
|
|
||||||
org.canAccessImportExport || (org.canCreateNewCollections && org.flexibleCollections),
|
|
||||||
)
|
|
||||||
.sort(Utils.getSortFunction(i18nService, "name")),
|
.sort(Utils.getSortFunction(i18nService, "name")),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ describe("ORGANIZATIONS state", () => {
|
|||||||
permissions: undefined,
|
permissions: undefined,
|
||||||
resetPasswordEnrolled: false,
|
resetPasswordEnrolled: false,
|
||||||
userId: "userId",
|
userId: "userId",
|
||||||
|
organizationUserId: "organizationUserId",
|
||||||
hasPublicAndPrivateKeys: false,
|
hasPublicAndPrivateKeys: false,
|
||||||
providerId: "providerId",
|
providerId: "providerId",
|
||||||
providerName: "providerName",
|
providerName: "providerName",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export class OrganizationData {
|
|||||||
permissions: PermissionsApi;
|
permissions: PermissionsApi;
|
||||||
resetPasswordEnrolled: boolean;
|
resetPasswordEnrolled: boolean;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
organizationUserId: string;
|
||||||
hasPublicAndPrivateKeys: boolean;
|
hasPublicAndPrivateKeys: boolean;
|
||||||
providerId: string;
|
providerId: string;
|
||||||
providerName: string;
|
providerName: string;
|
||||||
@@ -96,6 +97,7 @@ export class OrganizationData {
|
|||||||
this.permissions = response.permissions;
|
this.permissions = response.permissions;
|
||||||
this.resetPasswordEnrolled = response.resetPasswordEnrolled;
|
this.resetPasswordEnrolled = response.resetPasswordEnrolled;
|
||||||
this.userId = response.userId;
|
this.userId = response.userId;
|
||||||
|
this.organizationUserId = response.organizationUserId;
|
||||||
this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys;
|
this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys;
|
||||||
this.providerId = response.providerId;
|
this.providerId = response.providerId;
|
||||||
this.providerName = response.providerName;
|
this.providerName = response.providerName;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export class Organization {
|
|||||||
permissions: PermissionsApi;
|
permissions: PermissionsApi;
|
||||||
resetPasswordEnrolled: boolean;
|
resetPasswordEnrolled: boolean;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
organizationUserId: string;
|
||||||
hasPublicAndPrivateKeys: boolean;
|
hasPublicAndPrivateKeys: boolean;
|
||||||
providerId: string;
|
providerId: string;
|
||||||
providerName: string;
|
providerName: string;
|
||||||
@@ -113,6 +114,7 @@ export class Organization {
|
|||||||
this.permissions = obj.permissions;
|
this.permissions = obj.permissions;
|
||||||
this.resetPasswordEnrolled = obj.resetPasswordEnrolled;
|
this.resetPasswordEnrolled = obj.resetPasswordEnrolled;
|
||||||
this.userId = obj.userId;
|
this.userId = obj.userId;
|
||||||
|
this.organizationUserId = obj.organizationUserId;
|
||||||
this.hasPublicAndPrivateKeys = obj.hasPublicAndPrivateKeys;
|
this.hasPublicAndPrivateKeys = obj.hasPublicAndPrivateKeys;
|
||||||
this.providerId = obj.providerId;
|
this.providerId = obj.providerId;
|
||||||
this.providerName = obj.providerName;
|
this.providerName = obj.providerName;
|
||||||
@@ -140,16 +142,6 @@ export class Organization {
|
|||||||
return this.enabled && this.status === OrganizationUserStatusType.Confirmed;
|
return this.enabled && this.status === OrganizationUserStatusType.Confirmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether a user has Manager permissions or greater
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
* This is deprecated with the introduction of Flexible Collections.
|
|
||||||
*/
|
|
||||||
get isManager() {
|
|
||||||
return this.type === OrganizationUserType.Manager || this.isAdmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether a user has Admin permissions or greater
|
* Whether a user has Admin permissions or greater
|
||||||
*/
|
*/
|
||||||
@@ -177,19 +169,13 @@ export class Organization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get canCreateNewCollections() {
|
get canCreateNewCollections() {
|
||||||
if (this.flexibleCollections) {
|
return (
|
||||||
return (
|
!this.limitCollectionCreationDeletion || this.isAdmin || this.permissions.createNewCollections
|
||||||
!this.limitCollectionCreationDeletion ||
|
);
|
||||||
this.isAdmin ||
|
|
||||||
this.permissions.createNewCollections
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.isManager || this.permissions.createNewCollections;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canEditAnyCollection(flexibleCollectionsV1Enabled: boolean) {
|
canEditAnyCollection(flexibleCollectionsV1Enabled: boolean) {
|
||||||
if (!this.flexibleCollections || !flexibleCollectionsV1Enabled) {
|
if (!flexibleCollectionsV1Enabled) {
|
||||||
// Pre-Flexible Collections v1 logic
|
// Pre-Flexible Collections v1 logic
|
||||||
return this.isAdmin || this.permissions.editAnyCollection;
|
return this.isAdmin || this.permissions.editAnyCollection;
|
||||||
}
|
}
|
||||||
@@ -219,8 +205,8 @@ export class Organization {
|
|||||||
flexibleCollectionsV1Enabled: boolean,
|
flexibleCollectionsV1Enabled: boolean,
|
||||||
restrictProviderAccessFlagEnabled: boolean,
|
restrictProviderAccessFlagEnabled: boolean,
|
||||||
) {
|
) {
|
||||||
// Before Flexible Collections, any admin or anyone with editAnyCollection permission could edit all ciphers
|
// Before Flexible Collections V1, any admin or anyone with editAnyCollection permission could edit all ciphers
|
||||||
if (!this.flexibleCollections || !flexibleCollectionsV1Enabled || !this.flexibleCollections) {
|
if (!flexibleCollectionsV1Enabled) {
|
||||||
return this.isAdmin || this.permissions.editAnyCollection;
|
return this.isAdmin || this.permissions.editAnyCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,33 +253,6 @@ export class Organization {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* This is deprecated with the introduction of Flexible Collections.
|
|
||||||
* This will always return false if FlexibleCollections flag is on.
|
|
||||||
*/
|
|
||||||
get canEditAssignedCollections() {
|
|
||||||
return this.isManager || this.permissions.editAssignedCollections;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* This is deprecated with the introduction of Flexible Collections.
|
|
||||||
* This will always return false if FlexibleCollections flag is on.
|
|
||||||
*/
|
|
||||||
get canDeleteAssignedCollections() {
|
|
||||||
return this.isManager || this.permissions.deleteAssignedCollections;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* This is deprecated with the introduction of Flexible Collections.
|
|
||||||
* This will always return false if FlexibleCollections flag is on.
|
|
||||||
*/
|
|
||||||
get canViewAssignedCollections() {
|
|
||||||
return this.canDeleteAssignedCollections || this.canEditAssignedCollections;
|
|
||||||
}
|
|
||||||
|
|
||||||
get canManageGroups() {
|
get canManageGroups() {
|
||||||
return (this.isAdmin || this.permissions.manageGroups) && this.useGroups;
|
return (this.isAdmin || this.permissions.manageGroups) && this.useGroups;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
|||||||
permissions: PermissionsApi;
|
permissions: PermissionsApi;
|
||||||
resetPasswordEnrolled: boolean;
|
resetPasswordEnrolled: boolean;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
organizationUserId: string;
|
||||||
providerId: string;
|
providerId: string;
|
||||||
providerName: string;
|
providerName: string;
|
||||||
providerType?: ProviderType;
|
providerType?: ProviderType;
|
||||||
@@ -86,6 +87,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
|||||||
this.permissions = new PermissionsApi(this.getResponseProperty("permissions"));
|
this.permissions = new PermissionsApi(this.getResponseProperty("permissions"));
|
||||||
this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled");
|
this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled");
|
||||||
this.userId = this.getResponseProperty("UserId");
|
this.userId = this.getResponseProperty("UserId");
|
||||||
|
this.organizationUserId = this.getResponseProperty("OrganizationUserId");
|
||||||
this.providerId = this.getResponseProperty("ProviderId");
|
this.providerId = this.getResponseProperty("ProviderId");
|
||||||
this.providerName = this.getResponseProperty("ProviderName");
|
this.providerName = this.getResponseProperty("ProviderName");
|
||||||
this.providerType = this.getResponseProperty("ProviderType");
|
this.providerType = this.getResponseProperty("ProviderType");
|
||||||
|
|||||||
@@ -49,15 +49,11 @@ export class CollectionView implements View, ITreeNodeObject {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (org?.flexibleCollections) {
|
return (
|
||||||
return (
|
org?.canEditAllCiphers(v1FlexibleCollections, restrictProviderAccess) ||
|
||||||
org?.canEditAllCiphers(v1FlexibleCollections, restrictProviderAccess) ||
|
this.manage ||
|
||||||
this.manage ||
|
(this.assigned && !this.readOnly)
|
||||||
(this.assigned && !this.readOnly)
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return org?.canEditAnyCollection(false) || (org?.canEditAssignedCollections && this.assigned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -256,17 +256,14 @@ export class ImportComponent implements OnInit, OnDestroy {
|
|||||||
if (!this._importBlockedByPolicy) {
|
if (!this._importBlockedByPolicy) {
|
||||||
this.formGroup.controls.targetSelector.enable();
|
this.formGroup.controls.targetSelector.enable();
|
||||||
}
|
}
|
||||||
const flexCollectionEnabled =
|
|
||||||
organizations.find((x) => x.id == this.organizationId)?.flexibleCollections ?? false;
|
|
||||||
if (value) {
|
if (value) {
|
||||||
this.collections$ = Utils.asyncToObservable(() =>
|
this.collections$ = Utils.asyncToObservable(() =>
|
||||||
this.collectionService
|
this.collectionService
|
||||||
.getAllDecrypted()
|
.getAllDecrypted()
|
||||||
.then((decryptedCollections) =>
|
.then((decryptedCollections) =>
|
||||||
decryptedCollections
|
decryptedCollections
|
||||||
.filter(
|
.filter((c2) => c2.organizationId === value && c2.manage)
|
||||||
(c2) => c2.organizationId === value && (!flexCollectionEnabled || c2.manage),
|
|
||||||
)
|
|
||||||
.sort(Utils.getSortFunction(this.i18nService, "name")),
|
.sort(Utils.getSortFunction(this.i18nService, "name")),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user