mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 17:23:37 +00:00
Merge branch 'main' into autofill/pm-5189-fix-issues-present-with-inline-menu-rendering-in-iframes
This commit is contained in:
12
.github/workflows/build-browser.yml
vendored
12
.github/workflows/build-browser.yml
vendored
@@ -189,12 +189,12 @@ jobs:
|
|||||||
path: browser-source/apps/browser/dist/dist-chrome.zip
|
path: browser-source/apps/browser/dist/dist-chrome.zip
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
# - name: Upload Chrome MV3 artifact
|
- name: Upload Chrome MV3 artifact (DO NOT USE FOR PROD)
|
||||||
# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||||
# with:
|
with:
|
||||||
# name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip
|
name: DO-NOT-USE-FOR-PROD-dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||||
# path: browser-source/apps/browser/dist/dist-chrome-mv3.zip
|
path: browser-source/apps/browser/dist/dist-chrome-mv3.zip
|
||||||
# if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload Firefox artifact
|
- name: Upload Firefox artifact
|
||||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/
|
|||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.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";
|
||||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||||
|
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
||||||
import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service";
|
import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
|
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
|
||||||
import { OrganizationUserServiceImplementation } from "@bitwarden/common/admin-console/services/organization-user/organization-user.service.implementation";
|
import { OrganizationUserServiceImplementation } from "@bitwarden/common/admin-console/services/organization-user/organization-user.service.implementation";
|
||||||
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||||
|
import { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service";
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
|
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||||
@@ -232,6 +234,7 @@ export class Main {
|
|||||||
stateEventRunnerService: StateEventRunnerService;
|
stateEventRunnerService: StateEventRunnerService;
|
||||||
biometricStateService: BiometricStateService;
|
biometricStateService: BiometricStateService;
|
||||||
billingAccountProfileStateService: BillingAccountProfileStateService;
|
billingAccountProfileStateService: BillingAccountProfileStateService;
|
||||||
|
providerApiService: ProviderApiServiceAbstraction;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
let p = null;
|
let p = null;
|
||||||
@@ -692,6 +695,8 @@ export class Main {
|
|||||||
this.eventUploadService,
|
this.eventUploadService,
|
||||||
this.authService,
|
this.authService,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.providerApiService = new ProviderApiService(this.apiService);
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/web-vault",
|
"name": "@bitwarden/web-vault",
|
||||||
"version": "2024.4.0",
|
"version": "2024.4.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:oss": "webpack",
|
"build:oss": "webpack",
|
||||||
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",
|
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",
|
||||||
|
|||||||
@@ -31,7 +31,12 @@
|
|||||||
</bit-tab>
|
</bit-tab>
|
||||||
|
|
||||||
<bit-tab label="{{ 'members' | i18n }}">
|
<bit-tab label="{{ 'members' | i18n }}">
|
||||||
<p>{{ "editGroupMembersDesc" | i18n }}</p>
|
<p>
|
||||||
|
{{ "editGroupMembersDesc" | i18n }}
|
||||||
|
<span *ngIf="restrictGroupAccess$ | async">
|
||||||
|
{{ "restrictedGroupAccessDesc" | i18n }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
<bit-access-selector
|
<bit-access-selector
|
||||||
formControlName="members"
|
formControlName="members"
|
||||||
[items]="members"
|
[items]="members"
|
||||||
|
|||||||
@@ -1,15 +1,31 @@
|
|||||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from "@angular/core";
|
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
import { catchError, combineLatest, from, map, of, Subject, switchMap, takeUntil } from "rxjs";
|
import {
|
||||||
|
catchError,
|
||||||
|
combineLatest,
|
||||||
|
concatMap,
|
||||||
|
from,
|
||||||
|
map,
|
||||||
|
Observable,
|
||||||
|
of,
|
||||||
|
shareReplay,
|
||||||
|
Subject,
|
||||||
|
switchMap,
|
||||||
|
takeUntil,
|
||||||
|
} 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 { 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";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||||
import { CollectionData } from "@bitwarden/common/vault/models/data/collection.data";
|
import { CollectionData } from "@bitwarden/common/vault/models/data/collection.data";
|
||||||
import { Collection } from "@bitwarden/common/vault/models/domain/collection";
|
import { Collection } from "@bitwarden/common/vault/models/domain/collection";
|
||||||
@@ -88,10 +104,9 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
tabIndex: GroupAddEditTabType;
|
tabIndex: GroupAddEditTabType;
|
||||||
loading = true;
|
loading = true;
|
||||||
editMode = false;
|
|
||||||
title: string;
|
title: string;
|
||||||
collections: AccessItemView[] = [];
|
collections: AccessItemView[] = [];
|
||||||
members: AccessItemView[] = [];
|
members: Array<AccessItemView & { userId: UserId }> = [];
|
||||||
group: GroupView;
|
group: GroupView;
|
||||||
|
|
||||||
groupForm = this.formBuilder.group({
|
groupForm = this.formBuilder.group({
|
||||||
@@ -110,6 +125,10 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
return this.params.organizationId;
|
return this.params.organizationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected get editMode(): boolean {
|
||||||
|
return this.groupId != null;
|
||||||
|
}
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
private get orgCollections$() {
|
private get orgCollections$() {
|
||||||
@@ -134,7 +153,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get orgMembers$() {
|
private get orgMembers$(): Observable<Array<AccessItemView & { userId: UserId }>> {
|
||||||
return from(this.organizationUserService.getAllUsers(this.organizationId)).pipe(
|
return from(this.organizationUserService.getAllUsers(this.organizationId)).pipe(
|
||||||
map((response) =>
|
map((response) =>
|
||||||
response.data.map((m) => ({
|
response.data.map((m) => ({
|
||||||
@@ -145,13 +164,15 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
listName: m.name?.length > 0 ? `${m.name} (${m.email})` : m.email,
|
listName: m.name?.length > 0 ? `${m.name} (${m.email})` : m.email,
|
||||||
labelName: m.name || m.email,
|
labelName: m.name || m.email,
|
||||||
status: m.status,
|
status: m.status,
|
||||||
|
userId: m.userId as UserId,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get groupDetails$() {
|
private groupDetails$: Observable<GroupView | undefined> = of(this.editMode).pipe(
|
||||||
if (!this.editMode) {
|
concatMap((editMode) => {
|
||||||
|
if (!editMode) {
|
||||||
return of(undefined);
|
return of(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +193,26 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
return of(undefined);
|
return of(undefined);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}),
|
||||||
|
shareReplay({ refCount: false }),
|
||||||
|
);
|
||||||
|
|
||||||
|
restrictGroupAccess$ = combineLatest([
|
||||||
|
this.organizationService.get$(this.organizationId),
|
||||||
|
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
|
||||||
|
this.groupDetails$,
|
||||||
|
]).pipe(
|
||||||
|
map(
|
||||||
|
([organization, flexibleCollectionsV1Enabled, group]) =>
|
||||||
|
// Feature flag conditionals
|
||||||
|
flexibleCollectionsV1Enabled &&
|
||||||
|
organization.flexibleCollections &&
|
||||||
|
// Business logic conditionals
|
||||||
|
!organization.allowAdminAccessToAllCollectionItems &&
|
||||||
|
group !== undefined,
|
||||||
|
),
|
||||||
|
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DIALOG_DATA) private params: GroupAddEditDialogParams,
|
@Inject(DIALOG_DATA) private params: GroupAddEditDialogParams,
|
||||||
@@ -188,17 +228,25 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info;
|
this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.editMode = this.loading = this.groupId != null;
|
this.loading = true;
|
||||||
this.title = this.i18nService.t(this.editMode ? "editGroup" : "newGroup");
|
this.title = this.i18nService.t(this.editMode ? "editGroup" : "newGroup");
|
||||||
|
|
||||||
combineLatest([this.orgCollections$, this.orgMembers$, this.groupDetails$])
|
combineLatest([
|
||||||
|
this.orgCollections$,
|
||||||
|
this.orgMembers$,
|
||||||
|
this.groupDetails$,
|
||||||
|
this.restrictGroupAccess$,
|
||||||
|
this.accountService.activeAccount$,
|
||||||
|
])
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe(([collections, members, group]) => {
|
.subscribe(([collections, members, group, restrictGroupAccess, activeAccount]) => {
|
||||||
this.collections = collections;
|
this.collections = collections;
|
||||||
this.members = members;
|
this.members = members;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
@@ -224,6 +272,18 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the current user is not already in the group and cannot add themselves, remove them from the list
|
||||||
|
if (restrictGroupAccess) {
|
||||||
|
const organizationUserId = this.members.find((m) => m.userId === activeAccount.id).id;
|
||||||
|
const isAlreadyInGroup = this.groupForm.value.members.some(
|
||||||
|
(m) => m.id === organizationUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isAlreadyInGroup) {
|
||||||
|
this.members = this.members.filter((m) => m.id !== organizationUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { CollectionView } from "@bitwarden/common/vault/models/view/collection.v
|
|||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { CollectionAdminService } from "../../../../../vault/core/collection-admin.service";
|
import { CollectionAdminService } from "../../../../../vault/core/collection-admin.service";
|
||||||
|
import { CollectionAdminView } from "../../../../../vault/core/views/collection-admin.view";
|
||||||
import {
|
import {
|
||||||
CollectionAccessSelectionView,
|
CollectionAccessSelectionView,
|
||||||
GroupService,
|
GroupService,
|
||||||
@@ -206,25 +207,52 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
collections: this.collectionAdminService.getAll(this.params.organizationId),
|
collections: this.collectionAdminService.getAll(this.params.organizationId),
|
||||||
userDetails: userDetails$,
|
userDetails: userDetails$,
|
||||||
groups: groups$,
|
groups: groups$,
|
||||||
|
flexibleCollectionsV1Enabled: this.configService.getFeatureFlag$(
|
||||||
|
FeatureFlag.FlexibleCollectionsV1,
|
||||||
|
false,
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe(({ organization, collections, userDetails, groups }) => {
|
.subscribe(
|
||||||
|
({ organization, collections, userDetails, groups, flexibleCollectionsV1Enabled }) => {
|
||||||
this.setFormValidators(organization);
|
this.setFormValidators(organization);
|
||||||
|
|
||||||
this.collectionAccessItems = [].concat(
|
// Groups tab: populate available groups
|
||||||
collections.map((c) => mapCollectionToAccessItemView(c)),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.groupAccessItems = [].concat(
|
this.groupAccessItems = [].concat(
|
||||||
groups.map<AccessItemView>((g) => mapGroupToAccessItemView(g)),
|
groups.map<AccessItemView>((g) => mapGroupToAccessItemView(g)),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.params.organizationUserId) {
|
// Collections tab: Populate all available collections (including current user access where applicable)
|
||||||
this.loadOrganizationUser(userDetails, groups, collections);
|
this.collectionAccessItems = collections
|
||||||
|
.map((c) =>
|
||||||
|
mapCollectionToAccessItemView(
|
||||||
|
c,
|
||||||
|
organization,
|
||||||
|
flexibleCollectionsV1Enabled,
|
||||||
|
userDetails == null
|
||||||
|
? undefined
|
||||||
|
: c.users.find((access) => access.id === userDetails.id),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
// But remove collections that we can't assign access to, unless the user is already assigned
|
||||||
|
.filter(
|
||||||
|
(item) =>
|
||||||
|
!item.readonly || userDetails?.collections.some((access) => access.id == item.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (userDetails != null) {
|
||||||
|
this.loadOrganizationUser(
|
||||||
|
userDetails,
|
||||||
|
groups,
|
||||||
|
collections,
|
||||||
|
organization,
|
||||||
|
flexibleCollectionsV1Enabled,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setFormValidators(organization: Organization) {
|
private setFormValidators(organization: Organization) {
|
||||||
@@ -246,7 +274,9 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
private loadOrganizationUser(
|
private loadOrganizationUser(
|
||||||
userDetails: OrganizationUserAdminView,
|
userDetails: OrganizationUserAdminView,
|
||||||
groups: GroupView[],
|
groups: GroupView[],
|
||||||
collections: CollectionView[],
|
collections: CollectionAdminView[],
|
||||||
|
organization: Organization,
|
||||||
|
flexibleCollectionsV1Enabled: boolean,
|
||||||
) {
|
) {
|
||||||
if (!userDetails) {
|
if (!userDetails) {
|
||||||
throw new Error("Could not find user to edit.");
|
throw new Error("Could not find user to edit.");
|
||||||
@@ -295,13 +325,22 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Populate additional collection access via groups (rendered as separate rows from user access)
|
||||||
this.collectionAccessItems = this.collectionAccessItems.concat(
|
this.collectionAccessItems = this.collectionAccessItems.concat(
|
||||||
collectionsFromGroups.map(({ collection, accessSelection, group }) =>
|
collectionsFromGroups.map(({ collection, accessSelection, group }) =>
|
||||||
mapCollectionToAccessItemView(collection, accessSelection, group),
|
mapCollectionToAccessItemView(
|
||||||
|
collection,
|
||||||
|
organization,
|
||||||
|
flexibleCollectionsV1Enabled,
|
||||||
|
accessSelection,
|
||||||
|
group,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const accessSelections = mapToAccessSelections(userDetails);
|
// Set current collections and groups the user has access to (excluding collections the current user doesn't have
|
||||||
|
// permissions to change - they are included as readonly via the CollectionAccessItems)
|
||||||
|
const accessSelections = mapToAccessSelections(userDetails, this.collectionAccessItems);
|
||||||
const groupAccessSelections = mapToGroupAccessSelections(userDetails.groups);
|
const groupAccessSelections = mapToGroupAccessSelections(userDetails.groups);
|
||||||
|
|
||||||
this.formGroup.removeControl("emails");
|
this.formGroup.removeControl("emails");
|
||||||
@@ -573,6 +612,8 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
|
|
||||||
function mapCollectionToAccessItemView(
|
function mapCollectionToAccessItemView(
|
||||||
collection: CollectionView,
|
collection: CollectionView,
|
||||||
|
organization: Organization,
|
||||||
|
flexibleCollectionsV1Enabled: boolean,
|
||||||
accessSelection?: CollectionAccessSelectionView,
|
accessSelection?: CollectionAccessSelectionView,
|
||||||
group?: GroupView,
|
group?: GroupView,
|
||||||
): AccessItemView {
|
): AccessItemView {
|
||||||
@@ -581,7 +622,8 @@ function mapCollectionToAccessItemView(
|
|||||||
id: group ? `${collection.id}-${group.id}` : collection.id,
|
id: group ? `${collection.id}-${group.id}` : collection.id,
|
||||||
labelName: collection.name,
|
labelName: collection.name,
|
||||||
listName: collection.name,
|
listName: collection.name,
|
||||||
readonly: group !== undefined,
|
readonly:
|
||||||
|
group !== undefined || !collection.canEdit(organization, flexibleCollectionsV1Enabled),
|
||||||
readonlyPermission: accessSelection ? convertToPermission(accessSelection) : undefined,
|
readonlyPermission: accessSelection ? convertToPermission(accessSelection) : undefined,
|
||||||
viaGroupName: group?.name,
|
viaGroupName: group?.name,
|
||||||
};
|
};
|
||||||
@@ -596,16 +638,23 @@ function mapGroupToAccessItemView(group: GroupView): AccessItemView {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapToAccessSelections(user: OrganizationUserAdminView): AccessItemValue[] {
|
function mapToAccessSelections(
|
||||||
|
user: OrganizationUserAdminView,
|
||||||
|
items: AccessItemView[],
|
||||||
|
): AccessItemValue[] {
|
||||||
if (user == undefined) {
|
if (user == undefined) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return [].concat(
|
|
||||||
user.collections.map<AccessItemValue>((selection) => ({
|
return (
|
||||||
|
user.collections
|
||||||
|
// The FormControl value only represents editable collection access - exclude readonly access selections
|
||||||
|
.filter((selection) => !items.find((item) => item.id == selection.id).readonly)
|
||||||
|
.map<AccessItemValue>((selection) => ({
|
||||||
id: selection.id,
|
id: selection.id,
|
||||||
type: AccessItemType.Collection,
|
type: AccessItemType.Collection,
|
||||||
permission: convertToPermission(selection),
|
permission: convertToPermission(selection),
|
||||||
})),
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" class="container" ngNativeValidate>
|
||||||
|
<div class="row justify-content-md-center mt-5">
|
||||||
|
<div class="col-5">
|
||||||
|
<p class="lead text-center mb-4">{{ "deleteProvider" | i18n }}</p>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<app-callout type="warning">{{ "deleteProviderWarning" | i18n }}</app-callout>
|
||||||
|
<p class="text-center">
|
||||||
|
<strong>{{ name }}</strong>
|
||||||
|
</p>
|
||||||
|
<p>{{ "deleteProviderRecoverConfirmDesc" | i18n }}</p>
|
||||||
|
<hr />
|
||||||
|
<div class="d-flex">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-danger btn-block btn-submit"
|
||||||
|
[disabled]="form.loading"
|
||||||
|
>
|
||||||
|
<span>{{ "deleteProvider" | i18n }}</span>
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</button>
|
||||||
|
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
||||||
|
import { ProviderVerifyRecoverDeleteRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-verify-recover-delete.request";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-verify-recover-delete-provider",
|
||||||
|
templateUrl: "verify-recover-delete-provider.component.html",
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||||
|
export class VerifyRecoverDeleteProviderComponent implements OnInit {
|
||||||
|
name: string;
|
||||||
|
formPromise: Promise<any>;
|
||||||
|
|
||||||
|
private providerId: string;
|
||||||
|
private token: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private providerApiService: ProviderApiServiceAbstraction,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private logService: LogService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
const qParams = await firstValueFrom(this.route.queryParams);
|
||||||
|
if (qParams.providerId != null && qParams.token != null && qParams.name != null) {
|
||||||
|
this.providerId = qParams.providerId;
|
||||||
|
this.token = qParams.token;
|
||||||
|
this.name = qParams.name;
|
||||||
|
} else {
|
||||||
|
await this.router.navigate(["/"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
try {
|
||||||
|
const request = new ProviderVerifyRecoverDeleteRequest(this.token);
|
||||||
|
this.formPromise = this.providerApiService.providerRecoverDeleteToken(
|
||||||
|
this.providerId,
|
||||||
|
request,
|
||||||
|
);
|
||||||
|
await this.formPromise;
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"success",
|
||||||
|
this.i18nService.t("providerDeleted"),
|
||||||
|
this.i18nService.t("providerDeletedDesc"),
|
||||||
|
);
|
||||||
|
await this.router.navigate(["/"]);
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
<div class="d-flex tabbed-header">
|
<div class="tw-flex tw-justify-between tw-mb-2 tw-pb-2 tw-mt-6">
|
||||||
<h1>
|
<h2 bitTypography="h2">
|
||||||
{{ "billingHistory" | i18n }}
|
{{ "billingHistory" | i18n }}
|
||||||
</h1>
|
</h2>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
bitButton
|
bitButton
|
||||||
buttonType="secondary"
|
buttonType="secondary"
|
||||||
(click)="load()"
|
(click)="load()"
|
||||||
class="tw-ml-auto"
|
|
||||||
*ngIf="firstLoaded"
|
*ngIf="firstLoaded"
|
||||||
[disabled]="loading"
|
[disabled]="loading"
|
||||||
>
|
>
|
||||||
@@ -17,11 +16,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="!firstLoaded && loading">
|
<ng-container *ngIf="!firstLoaded && loading">
|
||||||
<i
|
<i
|
||||||
class="bwi bwi-spinner bwi-spin text-muted"
|
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||||
title="{{ 'loading' | i18n }}"
|
title="{{ 'loading' | i18n }}"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="billing">
|
<ng-container *ngIf="billing">
|
||||||
<app-billing-history [billing]="billing"></app-billing-history>
|
<app-billing-history [billing]="billing"></app-billing-history>
|
||||||
|
|||||||
@@ -170,8 +170,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel">
|
<ng-container *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel">
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<div class="d-flex" *ngIf="!showAdjustStorage">
|
<div class="d-flex">
|
||||||
<button bitButton type="button" buttonType="secondary" (click)="adjustStorage(true)">
|
<button bitButton type="button" buttonType="secondary" [bitAction]="adjustStorage(true)">
|
||||||
{{ "addStorage" | i18n }}
|
{{ "addStorage" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@@ -179,18 +179,11 @@
|
|||||||
type="button"
|
type="button"
|
||||||
buttonType="secondary"
|
buttonType="secondary"
|
||||||
class="tw-ml-1"
|
class="tw-ml-1"
|
||||||
(click)="adjustStorage(false)"
|
[bitAction]="adjustStorage(false)"
|
||||||
>
|
>
|
||||||
{{ "removeStorage" | i18n }}
|
{{ "removeStorage" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<app-adjust-storage
|
|
||||||
[storageGbPrice]="4"
|
|
||||||
[add]="adjustStorageAdd"
|
|
||||||
(onAdjusted)="closeStorage(true)"
|
|
||||||
(onCanceled)="closeStorage(false)"
|
|
||||||
*ngIf="showAdjustStorage"
|
|
||||||
></app-adjust-storage>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
|||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import {
|
||||||
|
AdjustStorageDialogResult,
|
||||||
|
openAdjustStorageDialog,
|
||||||
|
} from "../shared/adjust-storage.component";
|
||||||
import {
|
import {
|
||||||
OffboardingSurveyDialogResultType,
|
OffboardingSurveyDialogResultType,
|
||||||
openOffboardingSurvey,
|
openOffboardingSurvey,
|
||||||
@@ -24,7 +28,6 @@ export class UserSubscriptionComponent implements OnInit {
|
|||||||
loading = false;
|
loading = false;
|
||||||
firstLoaded = false;
|
firstLoaded = false;
|
||||||
adjustStorageAdd = true;
|
adjustStorageAdd = true;
|
||||||
showAdjustStorage = false;
|
|
||||||
showUpdateLicense = false;
|
showUpdateLicense = false;
|
||||||
sub: SubscriptionResponse;
|
sub: SubscriptionResponse;
|
||||||
selfHosted = false;
|
selfHosted = false;
|
||||||
@@ -144,19 +147,20 @@ export class UserSubscriptionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustStorage(add: boolean) {
|
adjustStorage = (add: boolean) => {
|
||||||
this.adjustStorageAdd = add;
|
return async () => {
|
||||||
this.showAdjustStorage = true;
|
const dialogRef = openAdjustStorageDialog(this.dialogService, {
|
||||||
}
|
data: {
|
||||||
|
storageGbPrice: 4,
|
||||||
closeStorage(load: boolean) {
|
add: add,
|
||||||
this.showAdjustStorage = false;
|
},
|
||||||
if (load) {
|
});
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
const result = await lastValueFrom(dialogRef.closed);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
if (result === AdjustStorageDialogResult.Adjusted) {
|
||||||
this.load();
|
await this.load();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
get subscriptionMarkedForCancel() {
|
get subscriptionMarkedForCancel() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
<div class="card card-org-plans">
|
<div
|
||||||
<div class="card-body">
|
class="tw-relative tw-flex tw-flex-col tw-min-w-0 tw-rounded tw-border tw-border-solid tw-border-secondary-300"
|
||||||
<button type="button" class="close" appA11yTitle="{{ 'cancel' | i18n }}" (click)="cancel()">
|
>
|
||||||
<span aria-hidden="true">×</span>
|
<div class="tw-flex-auto tw-p-5">
|
||||||
</button>
|
<button
|
||||||
<h2 class="card-body-header">{{ "changeBillingPlan" | i18n }}</h2>
|
bitIconButton="bwi-close"
|
||||||
<p class="mb-0">{{ "changeBillingPlanUpgrade" | i18n }}</p>
|
buttonType="main"
|
||||||
|
type="button"
|
||||||
|
size="small"
|
||||||
|
class="tw-float-right"
|
||||||
|
appA11yTitle="{{ 'cancel' | i18n }}"
|
||||||
|
(click)="cancel()"
|
||||||
|
></button>
|
||||||
|
<h2 bitTypography="h2">{{ "changeBillingPlan" | i18n }}</h2>
|
||||||
|
<p bitTypography="body1" class="tw-mb-0">{{ "changeBillingPlanUpgrade" | i18n }}</p>
|
||||||
<app-organization-plans
|
<app-organization-plans
|
||||||
[showFree]="false"
|
[showFree]="false"
|
||||||
[showCancel]="true"
|
[showCancel]="true"
|
||||||
|
|||||||
@@ -16,11 +16,11 @@
|
|||||||
<bit-container>
|
<bit-container>
|
||||||
<ng-container *ngIf="!firstLoaded && loading">
|
<ng-container *ngIf="!firstLoaded && loading">
|
||||||
<i
|
<i
|
||||||
class="bwi bwi-spinner bwi-spin text-muted"
|
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||||
title="{{ 'loading' | i18n }}"
|
title="{{ 'loading' | i18n }}"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
<span class="sr-only">{{ "loading" | i18n }}</span>
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="billing">
|
<ng-container *ngIf="billing">
|
||||||
<app-billing-history [billing]="billing"></app-billing-history>
|
<app-billing-history [billing]="billing"></app-billing-history>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
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 { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
|
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
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 { OrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/organization-create.request";
|
import { OrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/organization-create.request";
|
||||||
@@ -147,6 +148,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
|||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
|
private providerApiService: ProviderApiServiceAbstraction,
|
||||||
) {
|
) {
|
||||||
this.selfHosted = platformUtilsService.isSelfHost();
|
this.selfHosted = platformUtilsService.isSelfHost();
|
||||||
}
|
}
|
||||||
@@ -182,7 +184,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
|||||||
if (this.hasProvider) {
|
if (this.hasProvider) {
|
||||||
this.formGroup.controls.businessOwned.setValue(true);
|
this.formGroup.controls.businessOwned.setValue(true);
|
||||||
this.changedOwnedBusiness();
|
this.changedOwnedBusiness();
|
||||||
this.provider = await this.apiService.getProvider(this.providerId);
|
this.provider = await this.providerApiService.getProvider(this.providerId);
|
||||||
const providerDefaultPlan = this.passwordManagerPlans.find(
|
const providerDefaultPlan = this.passwordManagerPlans.find(
|
||||||
(plan) => plan.type === PlanType.TeamsAnnually,
|
(plan) => plan.type === PlanType.TeamsAnnually,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -175,23 +175,24 @@
|
|||||||
<bit-progress [barWidth]="storagePercentage" bgColor="success"></bit-progress>
|
<bit-progress [barWidth]="storagePercentage" bgColor="success"></bit-progress>
|
||||||
<ng-container *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel">
|
<ng-container *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel">
|
||||||
<div class="tw-mt-3">
|
<div class="tw-mt-3">
|
||||||
<div class="tw-flex tw-space-x-2" *ngIf="!showAdjustStorage">
|
<div class="tw-flex tw-space-x-2">
|
||||||
<button bitButton buttonType="secondary" type="button" (click)="adjustStorage(true)">
|
<button
|
||||||
|
bitButton
|
||||||
|
buttonType="secondary"
|
||||||
|
type="button"
|
||||||
|
[bitAction]="adjustStorage(true)"
|
||||||
|
>
|
||||||
{{ "addStorage" | i18n }}
|
{{ "addStorage" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button bitButton buttonType="secondary" type="button" (click)="adjustStorage(false)">
|
<button
|
||||||
|
bitButton
|
||||||
|
buttonType="secondary"
|
||||||
|
type="button"
|
||||||
|
[bitAction]="adjustStorage(false)"
|
||||||
|
>
|
||||||
{{ "removeStorage" | i18n }}
|
{{ "removeStorage" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<app-adjust-storage
|
|
||||||
[storageGbPrice]="storageGbPrice"
|
|
||||||
[add]="adjustStorageAdd"
|
|
||||||
[organizationId]="organizationId"
|
|
||||||
[interval]="billingInterval"
|
|
||||||
(onAdjusted)="closeStorage(true)"
|
|
||||||
(onCanceled)="closeStorage(false)"
|
|
||||||
*ngIf="showAdjustStorage"
|
|
||||||
></app-adjust-storage>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="showAdjustSecretsManager">
|
<ng-container *ngIf="showAdjustSecretsManager">
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
|||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import {
|
||||||
|
AdjustStorageDialogResult,
|
||||||
|
openAdjustStorageDialog,
|
||||||
|
} from "../shared/adjust-storage.component";
|
||||||
import {
|
import {
|
||||||
OffboardingSurveyDialogResultType,
|
OffboardingSurveyDialogResultType,
|
||||||
openOffboardingSurvey,
|
openOffboardingSurvey,
|
||||||
@@ -36,8 +40,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
userOrg: Organization;
|
userOrg: Organization;
|
||||||
showChangePlan = false;
|
showChangePlan = false;
|
||||||
showDownloadLicense = false;
|
showDownloadLicense = false;
|
||||||
adjustStorageAdd = true;
|
|
||||||
showAdjustStorage = false;
|
|
||||||
hasBillingSyncToken: boolean;
|
hasBillingSyncToken: boolean;
|
||||||
showAdjustSecretsManager = false;
|
showAdjustSecretsManager = false;
|
||||||
showSecretsManagerSubscribe = false;
|
showSecretsManagerSubscribe = false;
|
||||||
@@ -361,19 +363,22 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustStorage(add: boolean) {
|
adjustStorage = (add: boolean) => {
|
||||||
this.adjustStorageAdd = add;
|
return async () => {
|
||||||
this.showAdjustStorage = true;
|
const dialogRef = openAdjustStorageDialog(this.dialogService, {
|
||||||
}
|
data: {
|
||||||
|
storageGbPrice: this.storageGbPrice,
|
||||||
closeStorage(load: boolean) {
|
add: add,
|
||||||
this.showAdjustStorage = false;
|
organizationId: this.organizationId,
|
||||||
if (load) {
|
interval: this.billingInterval,
|
||||||
// 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
|
});
|
||||||
this.load();
|
const result = await lastValueFrom(dialogRef.closed);
|
||||||
}
|
if (result === AdjustStorageDialogResult.Adjusted) {
|
||||||
|
await this.load();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
removeSponsorship = async () => {
|
removeSponsorship = async () => {
|
||||||
const confirmed = await this.dialogService.openSimpleDialog({
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||||
|
<bit-dialog
|
||||||
|
dialogSize="large"
|
||||||
|
[title]="(currentType != null ? 'changePaymentMethod' : 'addPaymentMethod') | i18n"
|
||||||
|
>
|
||||||
|
<ng-container bitDialogContent>
|
||||||
|
<app-payment [hideBank]="!organizationId" [hideCredit]="true"></app-payment>
|
||||||
|
<app-tax-info (onCountryChanged)="changeCountry()"></app-tax-info>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container bitDialogFooter>
|
||||||
|
<button type="submit" bitButton bitFormButton buttonType="primary">
|
||||||
|
{{ "submit" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
bitFormButton
|
||||||
|
buttonType="secondary"
|
||||||
|
[bitDialogClose]="DialogResult.Cancelled"
|
||||||
|
>
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</bit-dialog>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject, ViewChild } from "@angular/core";
|
||||||
|
import { FormGroup } from "@angular/forms";
|
||||||
|
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
|
import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction";
|
||||||
|
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||||
|
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { PaymentComponent } from "./payment.component";
|
||||||
|
import { TaxInfoComponent } from "./tax-info.component";
|
||||||
|
|
||||||
|
export interface AdjustPaymentDialogData {
|
||||||
|
organizationId: string;
|
||||||
|
currentType: PaymentMethodType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AdjustPaymentDialogResult {
|
||||||
|
Adjusted = "adjusted",
|
||||||
|
Cancelled = "cancelled",
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "adjust-payment-dialog.component.html",
|
||||||
|
})
|
||||||
|
export class AdjustPaymentDialogComponent {
|
||||||
|
@ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent;
|
||||||
|
@ViewChild(TaxInfoComponent, { static: true }) taxInfoComponent: TaxInfoComponent;
|
||||||
|
|
||||||
|
organizationId: string;
|
||||||
|
currentType: PaymentMethodType;
|
||||||
|
paymentMethodType = PaymentMethodType;
|
||||||
|
|
||||||
|
protected DialogResult = AdjustPaymentDialogResult;
|
||||||
|
protected formGroup = new FormGroup({});
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private dialogRef: DialogRef,
|
||||||
|
@Inject(DIALOG_DATA) protected data: AdjustPaymentDialogData,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private logService: LogService,
|
||||||
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
|
private paymentMethodWarningService: PaymentMethodWarningService,
|
||||||
|
) {
|
||||||
|
this.organizationId = data.organizationId;
|
||||||
|
this.currentType = data.currentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
submit = async () => {
|
||||||
|
const request = new PaymentRequest();
|
||||||
|
const response = this.paymentComponent.createPaymentToken().then((result) => {
|
||||||
|
request.paymentToken = result[0];
|
||||||
|
request.paymentMethodType = result[1];
|
||||||
|
request.postalCode = this.taxInfoComponent.taxInfo.postalCode;
|
||||||
|
request.country = this.taxInfoComponent.taxInfo.country;
|
||||||
|
if (this.organizationId == null) {
|
||||||
|
return this.apiService.postAccountPayment(request);
|
||||||
|
} else {
|
||||||
|
request.taxId = this.taxInfoComponent.taxInfo.taxId;
|
||||||
|
request.state = this.taxInfoComponent.taxInfo.state;
|
||||||
|
request.line1 = this.taxInfoComponent.taxInfo.line1;
|
||||||
|
request.line2 = this.taxInfoComponent.taxInfo.line2;
|
||||||
|
request.city = this.taxInfoComponent.taxInfo.city;
|
||||||
|
request.state = this.taxInfoComponent.taxInfo.state;
|
||||||
|
return this.organizationApiService.updatePayment(this.organizationId, request);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await response;
|
||||||
|
if (this.organizationId) {
|
||||||
|
await this.paymentMethodWarningService.removeSubscriptionRisk(this.organizationId);
|
||||||
|
}
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"success",
|
||||||
|
null,
|
||||||
|
this.i18nService.t("updatedPaymentMethod"),
|
||||||
|
);
|
||||||
|
this.dialogRef.close(AdjustPaymentDialogResult.Adjusted);
|
||||||
|
};
|
||||||
|
|
||||||
|
changeCountry() {
|
||||||
|
if (this.taxInfoComponent.taxInfo.country === "US") {
|
||||||
|
this.paymentComponent.hideBank = !this.organizationId;
|
||||||
|
} else {
|
||||||
|
this.paymentComponent.hideBank = true;
|
||||||
|
if (this.paymentComponent.method === PaymentMethodType.BankAccount) {
|
||||||
|
this.paymentComponent.method = PaymentMethodType.Card;
|
||||||
|
this.paymentComponent.changeMethod();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strongly typed helper to open a AdjustPaymentDialog
|
||||||
|
* @param dialogService Instance of the dialog service that will be used to open the dialog
|
||||||
|
* @param config Configuration for the dialog
|
||||||
|
*/
|
||||||
|
export function openAdjustPaymentDialog(
|
||||||
|
dialogService: DialogService,
|
||||||
|
config: DialogConfig<AdjustPaymentDialogData>,
|
||||||
|
) {
|
||||||
|
return dialogService.open<AdjustPaymentDialogResult>(AdjustPaymentDialogComponent, config);
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<form #form class="card" (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
|
|
||||||
<div class="card-body">
|
|
||||||
<button type="button" class="close" appA11yTitle="{{ 'cancel' | i18n }}" (click)="cancel()">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
<h3 class="card-body-header">
|
|
||||||
{{ (currentType != null ? "changePaymentMethod" : "addPaymentMethod") | i18n }}
|
|
||||||
</h3>
|
|
||||||
<app-payment [hideBank]="!organizationId" [hideCredit]="true"></app-payment>
|
|
||||||
<app-tax-info (onCountryChanged)="changeCountry()"></app-tax-info>
|
|
||||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
|
||||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
|
||||||
<span>{{ "submit" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-outline-secondary" (click)="cancel()">
|
|
||||||
{{ "cancel" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import { Component, EventEmitter, Input, Output, ViewChild } from "@angular/core";
|
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
|
||||||
import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction";
|
|
||||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
|
||||||
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
|
|
||||||
import { PaymentComponent } from "./payment.component";
|
|
||||||
import { TaxInfoComponent } from "./tax-info.component";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-adjust-payment",
|
|
||||||
templateUrl: "adjust-payment.component.html",
|
|
||||||
})
|
|
||||||
export class AdjustPaymentComponent {
|
|
||||||
@ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent;
|
|
||||||
@ViewChild(TaxInfoComponent, { static: true }) taxInfoComponent: TaxInfoComponent;
|
|
||||||
|
|
||||||
@Input() currentType?: PaymentMethodType;
|
|
||||||
@Input() organizationId: string;
|
|
||||||
@Output() onAdjusted = new EventEmitter();
|
|
||||||
@Output() onCanceled = new EventEmitter();
|
|
||||||
|
|
||||||
paymentMethodType = PaymentMethodType;
|
|
||||||
formPromise: Promise<void>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private apiService: ApiService,
|
|
||||||
private i18nService: I18nService,
|
|
||||||
private platformUtilsService: PlatformUtilsService,
|
|
||||||
private logService: LogService,
|
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
|
||||||
private paymentMethodWarningService: PaymentMethodWarningService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async submit() {
|
|
||||||
try {
|
|
||||||
const request = new PaymentRequest();
|
|
||||||
this.formPromise = this.paymentComponent.createPaymentToken().then((result) => {
|
|
||||||
request.paymentToken = result[0];
|
|
||||||
request.paymentMethodType = result[1];
|
|
||||||
request.postalCode = this.taxInfoComponent.taxInfo.postalCode;
|
|
||||||
request.country = this.taxInfoComponent.taxInfo.country;
|
|
||||||
if (this.organizationId == null) {
|
|
||||||
return this.apiService.postAccountPayment(request);
|
|
||||||
} else {
|
|
||||||
request.taxId = this.taxInfoComponent.taxInfo.taxId;
|
|
||||||
request.state = this.taxInfoComponent.taxInfo.state;
|
|
||||||
request.line1 = this.taxInfoComponent.taxInfo.line1;
|
|
||||||
request.line2 = this.taxInfoComponent.taxInfo.line2;
|
|
||||||
request.city = this.taxInfoComponent.taxInfo.city;
|
|
||||||
request.state = this.taxInfoComponent.taxInfo.state;
|
|
||||||
return this.organizationApiService.updatePayment(this.organizationId, request);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await this.formPromise;
|
|
||||||
if (this.organizationId) {
|
|
||||||
await this.paymentMethodWarningService.removeSubscriptionRisk(this.organizationId);
|
|
||||||
}
|
|
||||||
this.platformUtilsService.showToast(
|
|
||||||
"success",
|
|
||||||
null,
|
|
||||||
this.i18nService.t("updatedPaymentMethod"),
|
|
||||||
);
|
|
||||||
this.onAdjusted.emit();
|
|
||||||
} catch (e) {
|
|
||||||
this.logService.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.onCanceled.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
changeCountry() {
|
|
||||||
if (this.taxInfoComponent.taxInfo.country === "US") {
|
|
||||||
this.paymentComponent.hideBank = !this.organizationId;
|
|
||||||
} else {
|
|
||||||
this.paymentComponent.hideBank = true;
|
|
||||||
if (this.paymentComponent.method === PaymentMethodType.BankAccount) {
|
|
||||||
this.paymentComponent.method = PaymentMethodType.Card;
|
|
||||||
this.paymentComponent.changeMethod();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +1,35 @@
|
|||||||
<form #form class="card" (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
|
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||||
<div class="card-body">
|
<bit-dialog dialogSize="default" [title]="(add ? 'addStorage' : 'removeStorage') | i18n">
|
||||||
<button type="button" class="close" appA11yTitle="{{ 'cancel' | i18n }}" (click)="cancel()">
|
<ng-container bitDialogContent>
|
||||||
<span aria-hidden="true">×</span>
|
<p bitTypography="body1">{{ (add ? "storageAddNote" : "storageRemoveNote") | i18n }}</p>
|
||||||
</button>
|
<div class="tw-grid tw-grid-cols-12">
|
||||||
<h3 class="card-body-header">{{ (add ? "addStorage" : "removeStorage") | i18n }}</h3>
|
<bit-form-field class="tw-col-span-7">
|
||||||
<div class="row">
|
<bit-label>{{ (add ? "gbStorageAdd" : "gbStorageRemove") | i18n }}</bit-label>
|
||||||
<div class="form-group col-6">
|
<input bitInput type="number" formControlName="storageAdjustment" />
|
||||||
<label for="storageAdjustment">{{
|
<bit-hint *ngIf="add">
|
||||||
(add ? "gbStorageAdd" : "gbStorageRemove") | i18n
|
<strong>{{ "total" | i18n }}:</strong>
|
||||||
}}</label>
|
{{ formGroup.get("storageAdjustment").value || 0 }} GB ×
|
||||||
<input
|
|
||||||
id="storageAdjustment"
|
|
||||||
class="form-control"
|
|
||||||
type="number"
|
|
||||||
name="StorageGbAdjustment"
|
|
||||||
[(ngModel)]="storageAdjustment"
|
|
||||||
min="0"
|
|
||||||
max="99"
|
|
||||||
step="1"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div *ngIf="add" class="mb-3">
|
|
||||||
<strong>{{ "total" | i18n }}:</strong> {{ storageAdjustment || 0 }} GB ×
|
|
||||||
{{ storageGbPrice | currency: "$" }} = {{ adjustedStorageTotal | currency: "$" }} /{{
|
{{ storageGbPrice | currency: "$" }} = {{ adjustedStorageTotal | currency: "$" }} /{{
|
||||||
interval | i18n
|
interval | i18n
|
||||||
}}
|
}}
|
||||||
|
</bit-hint>
|
||||||
|
</bit-form-field>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
</ng-container>
|
||||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
<ng-container bitDialogFooter>
|
||||||
<span>{{ "submit" | i18n }}</span>
|
<button type="submit" bitButton bitFormButton buttonType="primary">
|
||||||
|
{{ "submit" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary" (click)="cancel()">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
bitFormButton
|
||||||
|
buttonType="secondary"
|
||||||
|
[bitDialogClose]="DialogResult.Cancelled"
|
||||||
|
>
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<small class="d-block text-muted mt-3">
|
</ng-container>
|
||||||
{{ (add ? "storageAddNote" : "storageRemoveNote") | i18n }}
|
</bit-dialog>
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
<app-payment [showMethods]="false"></app-payment>
|
<app-payment [showMethods]="false"></app-payment>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { Component, EventEmitter, Input, Output, ViewChild } from "@angular/core";
|
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject, ViewChild } from "@angular/core";
|
||||||
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -8,27 +10,45 @@ import { StorageRequest } from "@bitwarden/common/models/request/storage.request
|
|||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { PaymentComponent } from "./payment.component";
|
import { PaymentComponent } from "./payment.component";
|
||||||
|
|
||||||
|
export interface AdjustStorageDialogData {
|
||||||
|
storageGbPrice: number;
|
||||||
|
add: boolean;
|
||||||
|
organizationId?: string;
|
||||||
|
interval?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AdjustStorageDialogResult {
|
||||||
|
Adjusted = "adjusted",
|
||||||
|
Cancelled = "cancelled",
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-adjust-storage",
|
|
||||||
templateUrl: "adjust-storage.component.html",
|
templateUrl: "adjust-storage.component.html",
|
||||||
})
|
})
|
||||||
export class AdjustStorageComponent {
|
export class AdjustStorageComponent {
|
||||||
@Input() storageGbPrice = 0;
|
storageGbPrice: number;
|
||||||
@Input() add = true;
|
add: boolean;
|
||||||
@Input() organizationId: string;
|
organizationId: string;
|
||||||
@Input() interval = "year";
|
interval: string;
|
||||||
@Output() onAdjusted = new EventEmitter<number>();
|
|
||||||
@Output() onCanceled = new EventEmitter();
|
|
||||||
|
|
||||||
@ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent;
|
@ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent;
|
||||||
|
|
||||||
storageAdjustment = 0;
|
protected DialogResult = AdjustStorageDialogResult;
|
||||||
formPromise: Promise<PaymentResponse | void>;
|
protected formGroup = new FormGroup({
|
||||||
|
storageAdjustment: new FormControl(0, [
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(0),
|
||||||
|
Validators.max(99),
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private dialogRef: DialogRef,
|
||||||
|
@Inject(DIALOG_DATA) protected data: AdjustStorageDialogData,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
@@ -36,12 +56,16 @@ export class AdjustStorageComponent {
|
|||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
) {}
|
) {
|
||||||
|
this.storageGbPrice = data.storageGbPrice;
|
||||||
|
this.add = data.add;
|
||||||
|
this.organizationId = data.organizationId;
|
||||||
|
this.interval = data.interval || "year";
|
||||||
|
}
|
||||||
|
|
||||||
async submit() {
|
submit = async () => {
|
||||||
try {
|
|
||||||
const request = new StorageRequest();
|
const request = new StorageRequest();
|
||||||
request.storageGbAdjustment = this.storageAdjustment;
|
request.storageGbAdjustment = this.formGroup.value.storageAdjustment;
|
||||||
if (!this.add) {
|
if (!this.add) {
|
||||||
request.storageGbAdjustment *= -1;
|
request.storageGbAdjustment *= -1;
|
||||||
}
|
}
|
||||||
@@ -50,12 +74,9 @@ export class AdjustStorageComponent {
|
|||||||
const action = async () => {
|
const action = async () => {
|
||||||
let response: Promise<PaymentResponse>;
|
let response: Promise<PaymentResponse>;
|
||||||
if (this.organizationId == null) {
|
if (this.organizationId == null) {
|
||||||
response = this.formPromise = this.apiService.postAccountStorage(request);
|
response = this.apiService.postAccountStorage(request);
|
||||||
} else {
|
} else {
|
||||||
response = this.formPromise = this.organizationApiService.updateStorage(
|
response = this.organizationApiService.updateStorage(this.organizationId, request);
|
||||||
this.organizationId,
|
|
||||||
request,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const result = await response;
|
const result = await response;
|
||||||
if (result != null && result.paymentIntentClientSecret != null) {
|
if (result != null && result.paymentIntentClientSecret != null) {
|
||||||
@@ -69,9 +90,8 @@ export class AdjustStorageComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.formPromise = action();
|
await action();
|
||||||
await this.formPromise;
|
this.dialogRef.close(AdjustStorageDialogResult.Adjusted);
|
||||||
this.onAdjusted.emit(this.storageAdjustment);
|
|
||||||
if (paymentFailed) {
|
if (paymentFailed) {
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"warning",
|
"warning",
|
||||||
@@ -89,16 +109,21 @@ export class AdjustStorageComponent {
|
|||||||
this.i18nService.t("adjustedStorage", request.storageGbAdjustment.toString()),
|
this.i18nService.t("adjustedStorage", request.storageGbAdjustment.toString()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
};
|
||||||
this.logService.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.onCanceled.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
get adjustedStorageTotal(): number {
|
get adjustedStorageTotal(): number {
|
||||||
return this.storageGbPrice * this.storageAdjustment;
|
return this.storageGbPrice * this.formGroup.value.storageAdjustment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strongly typed helper to open an AdjustStorageDialog
|
||||||
|
* @param dialogService Instance of the dialog service that will be used to open the dialog
|
||||||
|
* @param config Configuration for the dialog
|
||||||
|
*/
|
||||||
|
export function openAdjustStorageDialog(
|
||||||
|
dialogService: DialogService,
|
||||||
|
config: DialogConfig<AdjustStorageDialogData>,
|
||||||
|
) {
|
||||||
|
return dialogService.open<AdjustStorageDialogResult>(AdjustStorageComponent, config);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
<h2 class="mt-3">{{ "invoices" | i18n }}</h2>
|
<bit-section>
|
||||||
<p *ngIf="!invoices || !invoices.length">{{ "noInvoices" | i18n }}</p>
|
<h3 bitTypography="h3">{{ "invoices" | i18n }}</h3>
|
||||||
<table class="table mb-2" *ngIf="invoices && invoices.length">
|
<p bitTypography="body1" *ngIf="!invoices || !invoices.length">{{ "noInvoices" | i18n }}</p>
|
||||||
<tbody>
|
<bit-table>
|
||||||
<tr *ngFor="let i of invoices">
|
<ng-template body>
|
||||||
<td>{{ i.date | date: "mediumDate" }}</td>
|
<tr bitRow *ngFor="let i of invoices">
|
||||||
<td>
|
<td bitCell>{{ i.date | date: "mediumDate" }}</td>
|
||||||
|
<td bitCell>
|
||||||
<a
|
<a
|
||||||
href="{{ i.pdfUrl }}"
|
href="{{ i.pdfUrl }}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
class="mr-2"
|
class="tw-mr-2"
|
||||||
appA11yTitle="{{ 'downloadInvoice' | i18n }}"
|
appA11yTitle="{{ 'downloadInvoice' | i18n }}"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-file-pdf" aria-hidden="true"></i
|
<i class="bwi bwi-file-pdf" aria-hidden="true"></i
|
||||||
@@ -18,33 +19,37 @@
|
|||||||
{{ "invoiceNumber" | i18n: i.number }}</a
|
{{ "invoiceNumber" | i18n: i.number }}</a
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ i.amount | currency: "$" }}</td>
|
<td bitCell>{{ i.amount | currency: "$" }}</td>
|
||||||
<td>
|
<td bitCell>
|
||||||
<span *ngIf="i.paid">
|
<span *ngIf="i.paid">
|
||||||
<i class="bwi bwi-check text-success" aria-hidden="true"></i>
|
<i class="bwi bwi-check tw-text-success" aria-hidden="true"></i>
|
||||||
{{ "paid" | i18n }}
|
{{ "paid" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="!i.paid">
|
<span *ngIf="!i.paid">
|
||||||
<i class="bwi bwi-exclamation-circle text-muted" aria-hidden="true"></i>
|
<i class="bwi bwi-exclamation-circle tw-text-muted" aria-hidden="true"></i>
|
||||||
{{ "unpaid" | i18n }}
|
{{ "unpaid" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</ng-template>
|
||||||
</table>
|
</bit-table>
|
||||||
<h2 class="spaced-header">{{ "transactions" | i18n }}</h2>
|
</bit-section>
|
||||||
<p *ngIf="!transactions || !transactions.length">{{ "noTransactions" | i18n }}</p>
|
<bit-section>
|
||||||
<table class="table mb-2" *ngIf="transactions && transactions.length">
|
<h3 bitTypography="h3">{{ "transactions" | i18n }}</h3>
|
||||||
<tbody>
|
<p bitTypography="body1" *ngIf="!transactions || !transactions.length">
|
||||||
<tr *ngFor="let t of transactions">
|
{{ "noTransactions" | i18n }}
|
||||||
<td>{{ t.createdDate | date: "mediumDate" }}</td>
|
</p>
|
||||||
<td>
|
<bit-table *ngIf="transactions && transactions.length">
|
||||||
|
<ng-template body>
|
||||||
|
<tr bitRow *ngFor="let t of transactions">
|
||||||
|
<td bitCell>{{ t.createdDate | date: "mediumDate" }}</td>
|
||||||
|
<td bitCell>
|
||||||
<span *ngIf="t.type === transactionType.Charge || t.type === transactionType.Credit">
|
<span *ngIf="t.type === transactionType.Charge || t.type === transactionType.Credit">
|
||||||
{{ "chargeNoun" | i18n }}
|
{{ "chargeNoun" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="t.type === transactionType.Refund">{{ "refundNoun" | i18n }}</span>
|
<span *ngIf="t.type === transactionType.Refund">{{ "refundNoun" | i18n }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td bitCell>
|
||||||
<i
|
<i
|
||||||
class="bwi bwi-fw"
|
class="bwi bwi-fw"
|
||||||
*ngIf="t.paymentMethodType"
|
*ngIf="t.paymentMethodType"
|
||||||
@@ -56,10 +61,12 @@
|
|||||||
<td
|
<td
|
||||||
[ngClass]="{ 'text-strike': t.refunded }"
|
[ngClass]="{ 'text-strike': t.refunded }"
|
||||||
title="{{ (t.refunded ? 'refunded' : '') | i18n }}"
|
title="{{ (t.refunded ? 'refunded' : '') | i18n }}"
|
||||||
|
bitCell
|
||||||
>
|
>
|
||||||
{{ t.amount | currency: "$" }}
|
{{ t.amount | currency: "$" }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</ng-template>
|
||||||
</table>
|
</bit-table>
|
||||||
<small class="text-muted">* {{ "chargesStatement" | i18n: "BITWARDEN" }}</small>
|
<small class="tw-text-muted">* {{ "chargesStatement" | i18n: "BITWARDEN" }}</small>
|
||||||
|
</bit-section>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { HeaderModule } from "../../layouts/header/header.module";
|
|||||||
import { SharedModule } from "../../shared";
|
import { SharedModule } from "../../shared";
|
||||||
|
|
||||||
import { AddCreditComponent } from "./add-credit.component";
|
import { AddCreditComponent } from "./add-credit.component";
|
||||||
import { AdjustPaymentComponent } from "./adjust-payment.component";
|
import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog.component";
|
||||||
import { AdjustStorageComponent } from "./adjust-storage.component";
|
import { AdjustStorageComponent } from "./adjust-storage.component";
|
||||||
import { BillingHistoryComponent } from "./billing-history.component";
|
import { BillingHistoryComponent } from "./billing-history.component";
|
||||||
import { OffboardingSurveyComponent } from "./offboarding-survey.component";
|
import { OffboardingSurveyComponent } from "./offboarding-survey.component";
|
||||||
@@ -18,7 +18,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
|
|||||||
imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule],
|
imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule],
|
||||||
declarations: [
|
declarations: [
|
||||||
AddCreditComponent,
|
AddCreditComponent,
|
||||||
AdjustPaymentComponent,
|
AdjustPaymentDialogComponent,
|
||||||
AdjustStorageComponent,
|
AdjustStorageComponent,
|
||||||
BillingHistoryComponent,
|
BillingHistoryComponent,
|
||||||
PaymentMethodComponent,
|
PaymentMethodComponent,
|
||||||
|
|||||||
@@ -102,23 +102,9 @@
|
|||||||
{{ paymentSource.description }}
|
{{ paymentSource.description }}
|
||||||
</p>
|
</p>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<button
|
<button type="button" bitButton buttonType="secondary" [bitAction]="changePayment">
|
||||||
type="button"
|
|
||||||
bitButton
|
|
||||||
buttonType="secondary"
|
|
||||||
(click)="changePayment()"
|
|
||||||
*ngIf="!showAdjustPayment"
|
|
||||||
>
|
|
||||||
{{ (paymentSource ? "changePaymentMethod" : "addPaymentMethod") | i18n }}
|
{{ (paymentSource ? "changePaymentMethod" : "addPaymentMethod") | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<app-adjust-payment
|
|
||||||
[organizationId]="organizationId"
|
|
||||||
[currentType]="paymentSource != null ? paymentSource.type : null"
|
|
||||||
(onAdjusted)="closePayment(true)"
|
|
||||||
(onCanceled)="closePayment(false)"
|
|
||||||
*ngIf="showAdjustPayment"
|
|
||||||
>
|
|
||||||
</app-adjust-payment>
|
|
||||||
<p *ngIf="isUnpaid">{{ "paymentChargedWithUnpaidSubscription" | i18n }}</p>
|
<p *ngIf="isUnpaid">{{ "paymentChargedWithUnpaidSubscription" | i18n }}</p>
|
||||||
<ng-container *ngIf="forOrganization">
|
<ng-container *ngIf="forOrganization">
|
||||||
<h2 class="spaced-header">{{ "taxInformation" | i18n }}</h2>
|
<h2 class="spaced-header">{{ "taxInformation" | i18n }}</h2>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Component, OnInit, ViewChild } from "@angular/core";
|
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||||
import { FormBuilder, FormControl, Validators } from "@angular/forms";
|
import { FormBuilder, FormControl, Validators } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { lastValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
@@ -14,6 +15,10 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
|||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import {
|
||||||
|
AdjustPaymentDialogResult,
|
||||||
|
openAdjustPaymentDialog,
|
||||||
|
} from "./adjust-payment-dialog.component";
|
||||||
import { TaxInfoComponent } from "./tax-info.component";
|
import { TaxInfoComponent } from "./tax-info.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -25,7 +30,6 @@ export class PaymentMethodComponent implements OnInit {
|
|||||||
|
|
||||||
loading = false;
|
loading = false;
|
||||||
firstLoaded = false;
|
firstLoaded = false;
|
||||||
showAdjustPayment = false;
|
|
||||||
showAddCredit = false;
|
showAddCredit = false;
|
||||||
billing: BillingPaymentResponse;
|
billing: BillingPaymentResponse;
|
||||||
org: OrganizationSubscriptionResponse;
|
org: OrganizationSubscriptionResponse;
|
||||||
@@ -120,18 +124,18 @@ export class PaymentMethodComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changePayment() {
|
changePayment = async () => {
|
||||||
this.showAdjustPayment = true;
|
const dialogRef = openAdjustPaymentDialog(this.dialogService, {
|
||||||
}
|
data: {
|
||||||
|
organizationId: this.organizationId,
|
||||||
closePayment(load: boolean) {
|
currentType: this.paymentSource !== null ? this.paymentSource.type : null,
|
||||||
this.showAdjustPayment = false;
|
},
|
||||||
if (load) {
|
});
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
const result = await lastValueFrom(dialogRef.closed);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
if (result === AdjustPaymentDialogResult.Adjusted) {
|
||||||
this.load();
|
await this.load();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async verifyBank() {
|
async verifyBank() {
|
||||||
if (this.loading || !this.forOrganization) {
|
if (this.loading || !this.forOrganization) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { flagEnabled, Flags } from "../utils/flags";
|
|||||||
|
|
||||||
import { AcceptFamilySponsorshipComponent } from "./admin-console/organizations/sponsorships/accept-family-sponsorship.component";
|
import { AcceptFamilySponsorshipComponent } from "./admin-console/organizations/sponsorships/accept-family-sponsorship.component";
|
||||||
import { FamiliesForEnterpriseSetupComponent } from "./admin-console/organizations/sponsorships/families-for-enterprise-setup.component";
|
import { FamiliesForEnterpriseSetupComponent } from "./admin-console/organizations/sponsorships/families-for-enterprise-setup.component";
|
||||||
|
import { VerifyRecoverDeleteProviderComponent } from "./admin-console/providers/verify-recover-delete-provider.component";
|
||||||
import { CreateOrganizationComponent } from "./admin-console/settings/create-organization.component";
|
import { CreateOrganizationComponent } from "./admin-console/settings/create-organization.component";
|
||||||
import { SponsoredFamiliesComponent } from "./admin-console/settings/sponsored-families.component";
|
import { SponsoredFamiliesComponent } from "./admin-console/settings/sponsored-families.component";
|
||||||
import { AcceptOrganizationComponent } from "./auth/accept-organization.component";
|
import { AcceptOrganizationComponent } from "./auth/accept-organization.component";
|
||||||
@@ -156,6 +157,12 @@ const routes: Routes = [
|
|||||||
canActivate: [UnauthGuard],
|
canActivate: [UnauthGuard],
|
||||||
data: { titleId: "deleteAccount" },
|
data: { titleId: "deleteAccount" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "verify-recover-delete-provider",
|
||||||
|
component: VerifyRecoverDeleteProviderComponent,
|
||||||
|
canActivate: [UnauthGuard],
|
||||||
|
data: { titleId: "deleteAccount" },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "send/:sendId/:key",
|
path: "send/:sendId/:key",
|
||||||
component: AccessComponent,
|
component: AccessComponent,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { ReusedPasswordsReportComponent as OrgReusedPasswordsReportComponent } f
|
|||||||
import { UnsecuredWebsitesReportComponent as OrgUnsecuredWebsitesReportComponent } from "../admin-console/organizations/tools/unsecured-websites-report.component";
|
import { UnsecuredWebsitesReportComponent as OrgUnsecuredWebsitesReportComponent } from "../admin-console/organizations/tools/unsecured-websites-report.component";
|
||||||
import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from "../admin-console/organizations/tools/weak-passwords-report.component";
|
import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from "../admin-console/organizations/tools/weak-passwords-report.component";
|
||||||
import { ProvidersComponent } from "../admin-console/providers/providers.component";
|
import { ProvidersComponent } from "../admin-console/providers/providers.component";
|
||||||
|
import { VerifyRecoverDeleteProviderComponent } from "../admin-console/providers/verify-recover-delete-provider.component";
|
||||||
import { SponsoredFamiliesComponent } from "../admin-console/settings/sponsored-families.component";
|
import { SponsoredFamiliesComponent } from "../admin-console/settings/sponsored-families.component";
|
||||||
import { SponsoringOrgRowComponent } from "../admin-console/settings/sponsoring-org-row.component";
|
import { SponsoringOrgRowComponent } from "../admin-console/settings/sponsoring-org-row.component";
|
||||||
import { AcceptOrganizationComponent } from "../auth/accept-organization.component";
|
import { AcceptOrganizationComponent } from "../auth/accept-organization.component";
|
||||||
@@ -184,6 +185,7 @@ import { SharedModule } from "./shared.module";
|
|||||||
VerifyEmailComponent,
|
VerifyEmailComponent,
|
||||||
VerifyEmailTokenComponent,
|
VerifyEmailTokenComponent,
|
||||||
VerifyRecoverDeleteComponent,
|
VerifyRecoverDeleteComponent,
|
||||||
|
VerifyRecoverDeleteProviderComponent,
|
||||||
LowKdfComponent,
|
LowKdfComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
@@ -261,6 +263,7 @@ import { SharedModule } from "./shared.module";
|
|||||||
VerifyEmailComponent,
|
VerifyEmailComponent,
|
||||||
VerifyEmailTokenComponent,
|
VerifyEmailTokenComponent,
|
||||||
VerifyRecoverDeleteComponent,
|
VerifyRecoverDeleteComponent,
|
||||||
|
VerifyRecoverDeleteProviderComponent,
|
||||||
LowKdfComponent,
|
LowKdfComponent,
|
||||||
HeaderModule,
|
HeaderModule,
|
||||||
DangerZoneComponent,
|
DangerZoneComponent,
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ export class CollectionAdminService {
|
|||||||
view.groups = c.groups;
|
view.groups = c.groups;
|
||||||
view.users = c.users;
|
view.users = c.users;
|
||||||
view.assigned = c.assigned;
|
view.assigned = c.assigned;
|
||||||
|
view.readOnly = c.readOnly;
|
||||||
|
view.hidePasswords = c.hidePasswords;
|
||||||
|
view.manage = c.manage;
|
||||||
}
|
}
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
|
|||||||
@@ -7905,5 +7905,44 @@
|
|||||||
},
|
},
|
||||||
"unassignedItemsBannerSelfHost": {
|
"unassignedItemsBannerSelfHost": {
|
||||||
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in your All Vaults view across devices and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
|
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in your All Vaults view across devices and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
|
||||||
|
},
|
||||||
|
"restrictedGroupAccessDesc": {
|
||||||
|
"message": "You cannot add yourself to a group."
|
||||||
|
},
|
||||||
|
"deleteProvider": {
|
||||||
|
"message": "Delete provider"
|
||||||
|
},
|
||||||
|
"deleteProviderConfirmation": {
|
||||||
|
"message": "Deleting a provider is permanent and irreversible. Enter your master password to confirm the deletion of the provider and all associated data."
|
||||||
|
},
|
||||||
|
"deleteProviderName": {
|
||||||
|
"message": "Cannot delete $ID$",
|
||||||
|
"placeholders": {
|
||||||
|
"id": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "John Smith"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deleteProviderWarningDesc": {
|
||||||
|
"message": "You must unlink all clients before you can delete $ID$",
|
||||||
|
"placeholders": {
|
||||||
|
"id": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "John Smith"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"providerDeleted": {
|
||||||
|
"message": "Provider deleted"
|
||||||
|
},
|
||||||
|
"providerDeletedDesc": {
|
||||||
|
"message": "The Provider and all associated data has been deleted."
|
||||||
|
},
|
||||||
|
"deleteProviderRecoverConfirmDesc": {
|
||||||
|
"message": "You have requested to delete this Provider. Use the button below to confirm."
|
||||||
|
},
|
||||||
|
"deleteProviderWarning": {
|
||||||
|
"message": "Deleting your provider is permanent. It cannot be undone."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { OrganizationPlansComponent, TaxInfoComponent } from "@bitwarden/web-vau
|
|||||||
import { PaymentMethodWarningsModule } from "@bitwarden/web-vault/app/billing/shared";
|
import { PaymentMethodWarningsModule } from "@bitwarden/web-vault/app/billing/shared";
|
||||||
import { OssModule } from "@bitwarden/web-vault/app/oss.module";
|
import { OssModule } from "@bitwarden/web-vault/app/oss.module";
|
||||||
|
|
||||||
|
import { DangerZoneComponent } from "../../../../../../apps/web/src/app/auth/settings/account/danger-zone.component";
|
||||||
import { ManageClientOrganizationSubscriptionComponent } from "../../billing/providers/clients/manage-client-organization-subscription.component";
|
import { ManageClientOrganizationSubscriptionComponent } from "../../billing/providers/clients/manage-client-organization-subscription.component";
|
||||||
import { ManageClientOrganizationsComponent } from "../../billing/providers/clients/manage-client-organizations.component";
|
import { ManageClientOrganizationsComponent } from "../../billing/providers/clients/manage-client-organizations.component";
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ import { SetupComponent } from "./setup/setup.component";
|
|||||||
ProvidersLayoutComponent,
|
ProvidersLayoutComponent,
|
||||||
PaymentMethodWarningsModule,
|
PaymentMethodWarningsModule,
|
||||||
TaxInfoComponent,
|
TaxInfoComponent,
|
||||||
|
DangerZoneComponent,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AcceptProviderComponent,
|
AcceptProviderComponent,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<app-header></app-header>
|
<app-header></app-header>
|
||||||
|
<bit-container>
|
||||||
<div *ngIf="loading">
|
<div *ngIf="loading">
|
||||||
<i
|
<i
|
||||||
class="bwi bwi-spinner bwi-spin text-muted"
|
class="bwi bwi-spinner bwi-spin text-muted"
|
||||||
@@ -49,3 +49,10 @@
|
|||||||
<span>{{ "save" | i18n }}</span>
|
<span>{{ "save" | i18n }}</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<app-danger-zone *ngIf="enableDeleteProvider$ | async">
|
||||||
|
<button type="button" bitButton buttonType="danger" (click)="deleteProvider()">
|
||||||
|
{{ "deleteProvider" | i18n }}
|
||||||
|
</button>
|
||||||
|
</app-danger-zone>
|
||||||
|
</bit-container>
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
|
import { UserVerificationDialogComponent } from "@bitwarden/auth/angular";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
||||||
import { ProviderUpdateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-update.request";
|
import { ProviderUpdateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-update.request";
|
||||||
import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.response";
|
import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.response";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "provider-account",
|
selector: "provider-account",
|
||||||
@@ -23,6 +28,11 @@ export class AccountComponent {
|
|||||||
|
|
||||||
private providerId: string;
|
private providerId: string;
|
||||||
|
|
||||||
|
protected enableDeleteProvider$ = this.configService.getFeatureFlag$(
|
||||||
|
FeatureFlag.EnableDeleteProvider,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
@@ -30,6 +40,9 @@ export class AccountComponent {
|
|||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
|
private dialogService: DialogService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private providerApiService: ProviderApiServiceAbstraction,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@@ -38,7 +51,7 @@ export class AccountComponent {
|
|||||||
this.route.parent.parent.params.subscribe(async (params) => {
|
this.route.parent.parent.params.subscribe(async (params) => {
|
||||||
this.providerId = params.providerId;
|
this.providerId = params.providerId;
|
||||||
try {
|
try {
|
||||||
this.provider = await this.apiService.getProvider(this.providerId);
|
this.provider = await this.providerApiService.getProvider(this.providerId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(`Handled exception: ${e}`);
|
this.logService.error(`Handled exception: ${e}`);
|
||||||
}
|
}
|
||||||
@@ -53,7 +66,7 @@ export class AccountComponent {
|
|||||||
request.businessName = this.provider.businessName;
|
request.businessName = this.provider.businessName;
|
||||||
request.billingEmail = this.provider.billingEmail;
|
request.billingEmail = this.provider.billingEmail;
|
||||||
|
|
||||||
this.formPromise = this.apiService.putProvider(this.providerId, request).then(() => {
|
this.formPromise = this.providerApiService.putProvider(this.providerId, request).then(() => {
|
||||||
return this.syncService.fullSync(true);
|
return this.syncService.fullSync(true);
|
||||||
});
|
});
|
||||||
await this.formPromise;
|
await this.formPromise;
|
||||||
@@ -62,4 +75,60 @@ export class AccountComponent {
|
|||||||
this.logService.error(`Handled exception: ${e}`);
|
this.logService.error(`Handled exception: ${e}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteProvider() {
|
||||||
|
const providerClients = await this.apiService.getProviderClients(this.providerId);
|
||||||
|
if (providerClients.data != null && providerClients.data.length > 0) {
|
||||||
|
await this.dialogService.openSimpleDialog({
|
||||||
|
title: { key: "deleteProviderName", placeholders: [this.provider.name] },
|
||||||
|
content: { key: "deleteProviderWarningDesc", placeholders: [this.provider.name] },
|
||||||
|
acceptButtonText: { key: "ok" },
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userVerified = await this.verifyUser();
|
||||||
|
if (!userVerified) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.formPromise = this.providerApiService.deleteProvider(this.providerId);
|
||||||
|
try {
|
||||||
|
await this.formPromise;
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"success",
|
||||||
|
this.i18nService.t("providerDeleted"),
|
||||||
|
this.i18nService.t("providerDeletedDesc"),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(e);
|
||||||
|
}
|
||||||
|
this.formPromise = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async verifyUser(): Promise<boolean> {
|
||||||
|
const confirmDescription = "deleteProviderConfirmation";
|
||||||
|
const result = await UserVerificationDialogComponent.open(this.dialogService, {
|
||||||
|
title: "deleteProvider",
|
||||||
|
bodyText: confirmDescription,
|
||||||
|
confirmButtonOptions: {
|
||||||
|
text: "deleteProvider",
|
||||||
|
type: "danger",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle the result of the dialog based on user action and verification success
|
||||||
|
if (result.userAction === "cancel") {
|
||||||
|
// User cancelled the dialog
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// User confirmed the dialog so check verification success
|
||||||
|
if (!result.verificationSuccess) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ActivatedRoute, Router } from "@angular/router";
|
|||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
||||||
import { ProviderSetupRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-setup.request";
|
import { ProviderSetupRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-setup.request";
|
||||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
@@ -50,10 +50,10 @@ export class SetupComponent implements OnInit {
|
|||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
private apiService: ApiService,
|
|
||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
private validationService: ValidationService,
|
private validationService: ValidationService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
|
private providerApiService: ProviderApiServiceAbstraction,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -80,7 +80,7 @@ export class SetupComponent implements OnInit {
|
|||||||
|
|
||||||
// Check if provider exists, redirect if it does
|
// Check if provider exists, redirect if it does
|
||||||
try {
|
try {
|
||||||
const provider = await this.apiService.getProvider(this.providerId);
|
const provider = await this.providerApiService.getProvider(this.providerId);
|
||||||
if (provider.name != null) {
|
if (provider.name != null) {
|
||||||
// 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
|
||||||
@@ -128,7 +128,7 @@ export class SetupComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = await this.apiService.postProviderSetup(this.providerId, request);
|
const provider = await this.providerApiService.postProviderSetup(this.providerId, request);
|
||||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("providerSetup"));
|
this.platformUtilsService.showToast("success", null, this.i18nService.t("providerSetup"));
|
||||||
await this.syncService.fullSync(true);
|
await this.syncService.fullSync(true);
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
InternalPolicyService,
|
InternalPolicyService,
|
||||||
PolicyService as PolicyServiceAbstraction,
|
PolicyService as PolicyServiceAbstraction,
|
||||||
} from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
} from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
|
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
||||||
import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||||
import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service";
|
import { OrganizationApiService } from "@bitwarden/common/admin-console/services/organization/organization-api.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
|
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
|
||||||
@@ -47,6 +48,7 @@ import { DefaultOrganizationManagementPreferencesService } from "@bitwarden/comm
|
|||||||
import { OrganizationUserServiceImplementation } from "@bitwarden/common/admin-console/services/organization-user/organization-user.service.implementation";
|
import { OrganizationUserServiceImplementation } from "@bitwarden/common/admin-console/services/organization-user/organization-user.service.implementation";
|
||||||
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||||
|
import { ProviderApiService } from "@bitwarden/common/admin-console/services/provider/provider-api.service";
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
||||||
import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/account-api.service";
|
import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/account-api.service";
|
||||||
import {
|
import {
|
||||||
@@ -1115,6 +1117,11 @@ const safeProviders: SafeProvider[] = [
|
|||||||
useClass: LoggingErrorHandler,
|
useClass: LoggingErrorHandler,
|
||||||
deps: [],
|
deps: [],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: ProviderApiServiceAbstraction,
|
||||||
|
useClass: ProviderApiService,
|
||||||
|
deps: [ApiServiceAbstraction],
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
function encryptServiceFactory(
|
function encryptServiceFactory(
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import { OrganizationSponsorshipRedeemRequest } from "../admin-console/models/re
|
|||||||
import { OrganizationConnectionRequest } from "../admin-console/models/request/organization-connection.request";
|
import { OrganizationConnectionRequest } from "../admin-console/models/request/organization-connection.request";
|
||||||
import { ProviderAddOrganizationRequest } from "../admin-console/models/request/provider/provider-add-organization.request";
|
import { ProviderAddOrganizationRequest } from "../admin-console/models/request/provider/provider-add-organization.request";
|
||||||
import { ProviderOrganizationCreateRequest } from "../admin-console/models/request/provider/provider-organization-create.request";
|
import { ProviderOrganizationCreateRequest } from "../admin-console/models/request/provider/provider-organization-create.request";
|
||||||
import { ProviderSetupRequest } from "../admin-console/models/request/provider/provider-setup.request";
|
|
||||||
import { ProviderUpdateRequest } from "../admin-console/models/request/provider/provider-update.request";
|
|
||||||
import { ProviderUserAcceptRequest } from "../admin-console/models/request/provider/provider-user-accept.request";
|
import { ProviderUserAcceptRequest } from "../admin-console/models/request/provider/provider-user-accept.request";
|
||||||
import { ProviderUserBulkConfirmRequest } from "../admin-console/models/request/provider/provider-user-bulk-confirm.request";
|
import { ProviderUserBulkConfirmRequest } from "../admin-console/models/request/provider/provider-user-bulk-confirm.request";
|
||||||
import { ProviderUserBulkRequest } from "../admin-console/models/request/provider/provider-user-bulk.request";
|
import { ProviderUserBulkRequest } from "../admin-console/models/request/provider/provider-user-bulk.request";
|
||||||
@@ -29,7 +27,6 @@ import {
|
|||||||
ProviderUserResponse,
|
ProviderUserResponse,
|
||||||
ProviderUserUserDetailsResponse,
|
ProviderUserUserDetailsResponse,
|
||||||
} from "../admin-console/models/response/provider/provider-user.response";
|
} from "../admin-console/models/response/provider/provider-user.response";
|
||||||
import { ProviderResponse } from "../admin-console/models/response/provider/provider.response";
|
|
||||||
import { SelectionReadOnlyResponse } from "../admin-console/models/response/selection-read-only.response";
|
import { SelectionReadOnlyResponse } from "../admin-console/models/response/selection-read-only.response";
|
||||||
import { CreateAuthRequest } from "../auth/models/request/create-auth.request";
|
import { CreateAuthRequest } from "../auth/models/request/create-auth.request";
|
||||||
import { DeviceVerificationRequest } from "../auth/models/request/device-verification.request";
|
import { DeviceVerificationRequest } from "../auth/models/request/device-verification.request";
|
||||||
@@ -297,7 +294,6 @@ export abstract class ApiService {
|
|||||||
) => Promise<any>;
|
) => Promise<any>;
|
||||||
|
|
||||||
getGroupUsers: (organizationId: string, id: string) => Promise<string[]>;
|
getGroupUsers: (organizationId: string, id: string) => Promise<string[]>;
|
||||||
putGroupUsers: (organizationId: string, id: string, request: string[]) => Promise<any>;
|
|
||||||
deleteGroupUser: (organizationId: string, id: string, organizationUserId: string) => Promise<any>;
|
deleteGroupUser: (organizationId: string, id: string, organizationUserId: string) => Promise<any>;
|
||||||
|
|
||||||
getSync: () => Promise<SyncResponse>;
|
getSync: () => Promise<SyncResponse>;
|
||||||
@@ -373,10 +369,6 @@ export abstract class ApiService {
|
|||||||
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
||||||
getTaxRates: () => Promise<ListResponse<TaxRateResponse>>;
|
getTaxRates: () => Promise<ListResponse<TaxRateResponse>>;
|
||||||
|
|
||||||
postProviderSetup: (id: string, request: ProviderSetupRequest) => Promise<ProviderResponse>;
|
|
||||||
getProvider: (id: string) => Promise<ProviderResponse>;
|
|
||||||
putProvider: (id: string, request: ProviderUpdateRequest) => Promise<ProviderResponse>;
|
|
||||||
|
|
||||||
getProviderUsers: (providerId: string) => Promise<ListResponse<ProviderUserUserDetailsResponse>>;
|
getProviderUsers: (providerId: string) => Promise<ListResponse<ProviderUserUserDetailsResponse>>;
|
||||||
getProviderUser: (providerId: string, id: string) => Promise<ProviderUserResponse>;
|
getProviderUser: (providerId: string, id: string) => Promise<ProviderUserResponse>;
|
||||||
postProviderUserInvite: (providerId: string, request: ProviderUserInviteRequest) => Promise<any>;
|
postProviderUserInvite: (providerId: string, request: ProviderUserInviteRequest) => Promise<any>;
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { ProviderSetupRequest } from "../../models/request/provider/provider-setup.request";
|
||||||
|
import { ProviderUpdateRequest } from "../../models/request/provider/provider-update.request";
|
||||||
|
import { ProviderVerifyRecoverDeleteRequest } from "../../models/request/provider/provider-verify-recover-delete.request";
|
||||||
|
import { ProviderResponse } from "../../models/response/provider/provider.response";
|
||||||
|
|
||||||
|
export class ProviderApiServiceAbstraction {
|
||||||
|
postProviderSetup: (id: string, request: ProviderSetupRequest) => Promise<ProviderResponse>;
|
||||||
|
getProvider: (id: string) => Promise<ProviderResponse>;
|
||||||
|
putProvider: (id: string, request: ProviderUpdateRequest) => Promise<ProviderResponse>;
|
||||||
|
providerRecoverDeleteToken: (
|
||||||
|
organizationId: string,
|
||||||
|
request: ProviderVerifyRecoverDeleteRequest,
|
||||||
|
) => Promise<any>;
|
||||||
|
deleteProvider: (id: string) => Promise<void>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export class ProviderVerifyRecoverDeleteRequest {
|
||||||
|
token: string;
|
||||||
|
|
||||||
|
constructor(token: string) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { ApiService } from "../../../abstractions/api.service";
|
||||||
|
import { ProviderApiServiceAbstraction } from "../../abstractions/provider/provider-api.service.abstraction";
|
||||||
|
import { ProviderSetupRequest } from "../../models/request/provider/provider-setup.request";
|
||||||
|
import { ProviderUpdateRequest } from "../../models/request/provider/provider-update.request";
|
||||||
|
import { ProviderVerifyRecoverDeleteRequest } from "../../models/request/provider/provider-verify-recover-delete.request";
|
||||||
|
import { ProviderResponse } from "../../models/response/provider/provider.response";
|
||||||
|
|
||||||
|
export class ProviderApiService implements ProviderApiServiceAbstraction {
|
||||||
|
constructor(private apiService: ApiService) {}
|
||||||
|
async postProviderSetup(id: string, request: ProviderSetupRequest) {
|
||||||
|
const r = await this.apiService.send(
|
||||||
|
"POST",
|
||||||
|
"/providers/" + id + "/setup",
|
||||||
|
request,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return new ProviderResponse(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProvider(id: string) {
|
||||||
|
const r = await this.apiService.send("GET", "/providers/" + id, null, true, true);
|
||||||
|
return new ProviderResponse(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
async putProvider(id: string, request: ProviderUpdateRequest) {
|
||||||
|
const r = await this.apiService.send("PUT", "/providers/" + id, request, true, true);
|
||||||
|
return new ProviderResponse(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
providerRecoverDeleteToken(
|
||||||
|
providerId: string,
|
||||||
|
request: ProviderVerifyRecoverDeleteRequest,
|
||||||
|
): Promise<any> {
|
||||||
|
return this.apiService.send(
|
||||||
|
"POST",
|
||||||
|
"/providers/" + providerId + "/delete-recover-token",
|
||||||
|
request,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteProvider(id: string): Promise<void> {
|
||||||
|
await this.apiService.send("DELETE", "/providers/" + id, null, true, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ export enum FeatureFlag {
|
|||||||
EnableConsolidatedBilling = "enable-consolidated-billing",
|
EnableConsolidatedBilling = "enable-consolidated-billing",
|
||||||
AC1795_UpdatedSubscriptionStatusSection = "AC-1795_updated-subscription-status-section",
|
AC1795_UpdatedSubscriptionStatusSection = "AC-1795_updated-subscription-status-section",
|
||||||
UnassignedItemsBanner = "unassigned-items-banner",
|
UnassignedItemsBanner = "unassigned-items-banner",
|
||||||
|
EnableDeleteProvider = "AC-1218-delete-provider",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace this with a type safe lookup of the feature flag values in PM-2282
|
// Replace this with a type safe lookup of the feature flag values in PM-2282
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import { OrganizationSponsorshipRedeemRequest } from "../admin-console/models/re
|
|||||||
import { OrganizationConnectionRequest } from "../admin-console/models/request/organization-connection.request";
|
import { OrganizationConnectionRequest } from "../admin-console/models/request/organization-connection.request";
|
||||||
import { ProviderAddOrganizationRequest } from "../admin-console/models/request/provider/provider-add-organization.request";
|
import { ProviderAddOrganizationRequest } from "../admin-console/models/request/provider/provider-add-organization.request";
|
||||||
import { ProviderOrganizationCreateRequest } from "../admin-console/models/request/provider/provider-organization-create.request";
|
import { ProviderOrganizationCreateRequest } from "../admin-console/models/request/provider/provider-organization-create.request";
|
||||||
import { ProviderSetupRequest } from "../admin-console/models/request/provider/provider-setup.request";
|
|
||||||
import { ProviderUpdateRequest } from "../admin-console/models/request/provider/provider-update.request";
|
|
||||||
import { ProviderUserAcceptRequest } from "../admin-console/models/request/provider/provider-user-accept.request";
|
import { ProviderUserAcceptRequest } from "../admin-console/models/request/provider/provider-user-accept.request";
|
||||||
import { ProviderUserBulkConfirmRequest } from "../admin-console/models/request/provider/provider-user-bulk-confirm.request";
|
import { ProviderUserBulkConfirmRequest } from "../admin-console/models/request/provider/provider-user-bulk-confirm.request";
|
||||||
import { ProviderUserBulkRequest } from "../admin-console/models/request/provider/provider-user-bulk.request";
|
import { ProviderUserBulkRequest } from "../admin-console/models/request/provider/provider-user-bulk.request";
|
||||||
@@ -32,7 +30,6 @@ import {
|
|||||||
ProviderUserResponse,
|
ProviderUserResponse,
|
||||||
ProviderUserUserDetailsResponse,
|
ProviderUserUserDetailsResponse,
|
||||||
} from "../admin-console/models/response/provider/provider-user.response";
|
} from "../admin-console/models/response/provider/provider-user.response";
|
||||||
import { ProviderResponse } from "../admin-console/models/response/provider/provider.response";
|
|
||||||
import { SelectionReadOnlyResponse } from "../admin-console/models/response/selection-read-only.response";
|
import { SelectionReadOnlyResponse } from "../admin-console/models/response/selection-read-only.response";
|
||||||
import { TokenService } from "../auth/abstractions/token.service";
|
import { TokenService } from "../auth/abstractions/token.service";
|
||||||
import { CreateAuthRequest } from "../auth/models/request/create-auth.request";
|
import { CreateAuthRequest } from "../auth/models/request/create-auth.request";
|
||||||
@@ -866,16 +863,6 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
async putGroupUsers(organizationId: string, id: string, request: string[]): Promise<any> {
|
|
||||||
await this.send(
|
|
||||||
"PUT",
|
|
||||||
"/organizations/" + organizationId + "/groups/" + id + "/users",
|
|
||||||
request,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteGroupUser(organizationId: string, id: string, organizationUserId: string): Promise<any> {
|
deleteGroupUser(organizationId: string, id: string, organizationUserId: string): Promise<any> {
|
||||||
return this.send(
|
return this.send(
|
||||||
"DELETE",
|
"DELETE",
|
||||||
@@ -1161,23 +1148,6 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send("DELETE", "/organizations/connections/" + id, null, true, false);
|
return this.send("DELETE", "/organizations/connections/" + id, null, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provider APIs
|
|
||||||
|
|
||||||
async postProviderSetup(id: string, request: ProviderSetupRequest) {
|
|
||||||
const r = await this.send("POST", "/providers/" + id + "/setup", request, true, true);
|
|
||||||
return new ProviderResponse(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getProvider(id: string) {
|
|
||||||
const r = await this.send("GET", "/providers/" + id, null, true, true);
|
|
||||||
return new ProviderResponse(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
async putProvider(id: string, request: ProviderUpdateRequest) {
|
|
||||||
const r = await this.send("PUT", "/providers/" + id, request, true, true);
|
|
||||||
return new ProviderResponse(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provider User APIs
|
// Provider User APIs
|
||||||
|
|
||||||
async getProviderUsers(
|
async getProviderUsers(
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ export class CollectionDetailsResponse extends CollectionResponse {
|
|||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
manage: boolean;
|
manage: boolean;
|
||||||
hidePasswords: boolean;
|
hidePasswords: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating the user has been explicitly assigned to this Collection
|
||||||
|
*/
|
||||||
assigned: boolean;
|
assigned: boolean;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
@@ -35,15 +39,10 @@ export class CollectionDetailsResponse extends CollectionResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CollectionAccessDetailsResponse extends CollectionResponse {
|
export class CollectionAccessDetailsResponse extends CollectionDetailsResponse {
|
||||||
groups: SelectionReadOnlyResponse[] = [];
|
groups: SelectionReadOnlyResponse[] = [];
|
||||||
users: SelectionReadOnlyResponse[] = [];
|
users: SelectionReadOnlyResponse[] = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag indicating the user has been explicitly assigned to this Collection
|
|
||||||
*/
|
|
||||||
assigned: boolean;
|
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.assigned = this.getResponseProperty("Assigned") || false;
|
this.assigned = this.getResponseProperty("Assigned") || false;
|
||||||
|
|||||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -247,7 +247,7 @@
|
|||||||
},
|
},
|
||||||
"apps/web": {
|
"apps/web": {
|
||||||
"name": "@bitwarden/web-vault",
|
"name": "@bitwarden/web-vault",
|
||||||
"version": "2024.4.0"
|
"version": "2024.4.1"
|
||||||
},
|
},
|
||||||
"libs/admin-console": {
|
"libs/admin-console": {
|
||||||
"name": "@bitwarden/admin-console",
|
"name": "@bitwarden/admin-console",
|
||||||
|
|||||||
Reference in New Issue
Block a user