mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-02 18:17:46 +01:00
[PM-11404] Account Management: Prevent a verified user from purging their vault (#11411)
* Update AccountService to include a method for setting the managedByOrganizationId * Update AccountComponent to conditionally show the purgeVault button based on a feature flag and if the user is managed by an organization * Add missing method to FakeAccountService * Remove the setAccountManagedByOrganizationId method from the AccountService abstract class. * Refactor AccountComponent to use OrganizationService to check for managing organization * Rename managesActiveUser to userIsManagedByOrganization * Refactor userIsManagedByOrganization property to be non-nullable in organization data and response models * Refactor organization.data.spec.ts to include non-nullable userIsManagedByOrganization property
This commit is contained in:
parent
a5f856da2a
commit
97e195cd7b
@ -12,7 +12,13 @@
|
|||||||
<button type="button" bitButton buttonType="danger" (click)="deauthorizeSessions()">
|
<button type="button" bitButton buttonType="danger" (click)="deauthorizeSessions()">
|
||||||
{{ "deauthorizeSessions" | i18n }}
|
{{ "deauthorizeSessions" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitButton buttonType="danger" [bitAction]="purgeVault">
|
<button
|
||||||
|
*ngIf="showPurgeVault$ | async"
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
buttonType="danger"
|
||||||
|
[bitAction]="purgeVault"
|
||||||
|
>
|
||||||
{{ "purgeVault" | i18n }}
|
{{ "purgeVault" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitButton buttonType="danger" [bitAction]="deleteAccount">
|
<button type="button" bitButton buttonType="danger" [bitAction]="deleteAccount">
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { lastValueFrom } from "rxjs";
|
import { lastValueFrom, map, Observable, of, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { PurgeVaultComponent } from "../../../vault/settings/purge-vault.component";
|
import { PurgeVaultComponent } from "../../../vault/settings/purge-vault.component";
|
||||||
@ -19,15 +22,32 @@ export class AccountComponent implements OnInit {
|
|||||||
deauthModalRef: ViewContainerRef;
|
deauthModalRef: ViewContainerRef;
|
||||||
|
|
||||||
showChangeEmail = true;
|
showChangeEmail = true;
|
||||||
|
showPurgeVault$: Observable<boolean>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private modalService: ModalService,
|
private modalService: ModalService,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private userVerificationService: UserVerificationService,
|
private userVerificationService: UserVerificationService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private organizationService: OrganizationService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.showChangeEmail = await this.userVerificationService.hasMasterPassword();
|
this.showChangeEmail = await this.userVerificationService.hasMasterPassword();
|
||||||
|
this.showPurgeVault$ = this.configService
|
||||||
|
.getFeatureFlag$(FeatureFlag.AccountDeprovisioning)
|
||||||
|
.pipe(
|
||||||
|
switchMap((isAccountDeprovisioningEnabled) =>
|
||||||
|
isAccountDeprovisioningEnabled
|
||||||
|
? this.organizationService.organizations$.pipe(
|
||||||
|
map(
|
||||||
|
(organizations) =>
|
||||||
|
!organizations.some((o) => o.userIsManagedByOrganization === true),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: of(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deauthorizeSessions() {
|
async deauthorizeSessions() {
|
||||||
|
@ -57,6 +57,7 @@ describe("ORGANIZATIONS state", () => {
|
|||||||
limitCollectionCreationDeletion: false,
|
limitCollectionCreationDeletion: false,
|
||||||
allowAdminAccessToAllCollectionItems: false,
|
allowAdminAccessToAllCollectionItems: false,
|
||||||
familySponsorshipLastSyncDate: new Date(),
|
familySponsorshipLastSyncDate: new Date(),
|
||||||
|
userIsManagedByOrganization: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult)));
|
const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult)));
|
||||||
|
@ -57,6 +57,7 @@ export class OrganizationData {
|
|||||||
// Deprecated: https://bitwarden.atlassian.net/browse/PM-10863
|
// Deprecated: https://bitwarden.atlassian.net/browse/PM-10863
|
||||||
limitCollectionCreationDeletion: boolean;
|
limitCollectionCreationDeletion: boolean;
|
||||||
allowAdminAccessToAllCollectionItems: boolean;
|
allowAdminAccessToAllCollectionItems: boolean;
|
||||||
|
userIsManagedByOrganization: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
response?: ProfileOrganizationResponse,
|
response?: ProfileOrganizationResponse,
|
||||||
@ -118,6 +119,7 @@ export class OrganizationData {
|
|||||||
// Deprecated: https://bitwarden.atlassian.net/browse/PM-10863
|
// Deprecated: https://bitwarden.atlassian.net/browse/PM-10863
|
||||||
this.limitCollectionCreationDeletion = response.limitCollectionCreationDeletion;
|
this.limitCollectionCreationDeletion = response.limitCollectionCreationDeletion;
|
||||||
this.allowAdminAccessToAllCollectionItems = response.allowAdminAccessToAllCollectionItems;
|
this.allowAdminAccessToAllCollectionItems = response.allowAdminAccessToAllCollectionItems;
|
||||||
|
this.userIsManagedByOrganization = response.userIsManagedByOrganization;
|
||||||
|
|
||||||
this.isMember = options.isMember;
|
this.isMember = options.isMember;
|
||||||
this.isProviderUser = options.isProviderUser;
|
this.isProviderUser = options.isProviderUser;
|
||||||
|
@ -77,6 +77,12 @@ export class Organization {
|
|||||||
* Refers to the ability for an owner/admin to access all collection items, regardless of assigned collections
|
* Refers to the ability for an owner/admin to access all collection items, regardless of assigned collections
|
||||||
*/
|
*/
|
||||||
allowAdminAccessToAllCollectionItems: boolean;
|
allowAdminAccessToAllCollectionItems: boolean;
|
||||||
|
/**
|
||||||
|
* Indicates if this organization manages the user.
|
||||||
|
* A user is considered managed by an organization if their email domain
|
||||||
|
* matches one of the verified domains of that organization, and the user is a member of it.
|
||||||
|
*/
|
||||||
|
userIsManagedByOrganization: boolean;
|
||||||
|
|
||||||
constructor(obj?: OrganizationData) {
|
constructor(obj?: OrganizationData) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
@ -134,6 +140,7 @@ export class Organization {
|
|||||||
// Deprecated: https://bitwarden.atlassian.net/browse/PM-10863
|
// Deprecated: https://bitwarden.atlassian.net/browse/PM-10863
|
||||||
this.limitCollectionCreationDeletion = obj.limitCollectionCreationDeletion;
|
this.limitCollectionCreationDeletion = obj.limitCollectionCreationDeletion;
|
||||||
this.allowAdminAccessToAllCollectionItems = obj.allowAdminAccessToAllCollectionItems;
|
this.allowAdminAccessToAllCollectionItems = obj.allowAdminAccessToAllCollectionItems;
|
||||||
|
this.userIsManagedByOrganization = obj.userIsManagedByOrganization;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canAccess() {
|
get canAccess() {
|
||||||
|
@ -54,6 +54,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
|||||||
// Deprecated: https://bitwarden.atlassian.net/browse/PM-10863
|
// Deprecated: https://bitwarden.atlassian.net/browse/PM-10863
|
||||||
limitCollectionCreationDeletion: boolean;
|
limitCollectionCreationDeletion: boolean;
|
||||||
allowAdminAccessToAllCollectionItems: boolean;
|
allowAdminAccessToAllCollectionItems: boolean;
|
||||||
|
userIsManagedByOrganization: boolean;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
@ -121,5 +122,6 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
|||||||
this.allowAdminAccessToAllCollectionItems = this.getResponseProperty(
|
this.allowAdminAccessToAllCollectionItems = this.getResponseProperty(
|
||||||
"AllowAdminAccessToAllCollectionItems",
|
"AllowAdminAccessToAllCollectionItems",
|
||||||
);
|
);
|
||||||
|
this.userIsManagedByOrganization = this.getResponseProperty("UserIsManagedByOrganization");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ export class ProfileResponse extends BaseResponse {
|
|||||||
securityStamp: string;
|
securityStamp: string;
|
||||||
forcePasswordReset: boolean;
|
forcePasswordReset: boolean;
|
||||||
usesKeyConnector: boolean;
|
usesKeyConnector: boolean;
|
||||||
managedByOrganizationId?: string | null;
|
|
||||||
organizations: ProfileOrganizationResponse[] = [];
|
organizations: ProfileOrganizationResponse[] = [];
|
||||||
providers: ProfileProviderResponse[] = [];
|
providers: ProfileProviderResponse[] = [];
|
||||||
providerOrganizations: ProfileProviderOrganizationResponse[] = [];
|
providerOrganizations: ProfileProviderOrganizationResponse[] = [];
|
||||||
@ -43,7 +42,6 @@ export class ProfileResponse extends BaseResponse {
|
|||||||
this.securityStamp = this.getResponseProperty("SecurityStamp");
|
this.securityStamp = this.getResponseProperty("SecurityStamp");
|
||||||
this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset") ?? false;
|
this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset") ?? false;
|
||||||
this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false;
|
this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false;
|
||||||
this.managedByOrganizationId = this.getResponseProperty("ManagedByOrganizationId");
|
|
||||||
|
|
||||||
const organizations = this.getResponseProperty("Organizations");
|
const organizations = this.getResponseProperty("Organizations");
|
||||||
if (organizations != null) {
|
if (organizations != null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user