1
0
mirror of https://github.com/bitwarden/browser synced 2026-03-01 19:11:22 +00:00

Merge branch 'main' into ps/extension-refresh

This commit is contained in:
Victoria League
2024-10-17 09:30:31 -04:00
committed by GitHub
53 changed files with 549 additions and 253 deletions

View File

@@ -2,15 +2,7 @@
{{ "personalOwnershipExemption" | i18n }}
</bit-callout>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="enabled"
[formControl]="enabled"
name="Enabled"
/>
<label class="form-check-label" for="enabled">{{ "turnOn" | i18n }}</label>
</div>
</div>
<bit-form-control>
<input type="checkbox" bitCheckbox [formControl]="enabled" id="enabled" />
<bit-label>{{ "turnOn" | i18n }}</bit-label>
</bit-form-control>

View File

@@ -52,7 +52,11 @@
<form
*ngIf="org && !loading"
[bitSubmit]="submitCollectionManagement"
[formGroup]="collectionManagementFormGroup"
[formGroup]="
limitCollectionCreationDeletionSplitFeatureFlagIsEnabled
? collectionManagementFormGroup_VNext
: collectionManagementFormGroup
"
>
<h1 bitTypography="h1" class="tw-mt-16 tw-pb-2.5">{{ "collectionManagement" | i18n }}</h1>
<p bitTypography="body1">{{ "collectionManagementDesc" | i18n }}</p>
@@ -60,12 +64,24 @@
<bit-label>{{ "allowAdminAccessToAllCollectionItemsDesc" | i18n }}</bit-label>
<input type="checkbox" bitCheckbox formControlName="allowAdminAccessToAllCollectionItems" />
</bit-form-control>
<bit-form-control>
<bit-label>{{ "limitCollectionCreationDeletionDesc" | i18n }}</bit-label>
<input type="checkbox" bitCheckbox formControlName="limitCollectionCreationDeletion" />
</bit-form-control>
<ng-container *ngIf="limitCollectionCreationDeletionSplitFeatureFlagIsEnabled">
<bit-form-control>
<bit-label>{{ "limitCollectionCreationDesc" | i18n }}</bit-label>
<input type="checkbox" bitCheckbox formControlName="limitCollectionCreation" />
</bit-form-control>
<bit-form-control>
<bit-label>{{ "limitCollectionDeletionDesc" | i18n }}</bit-label>
<input type="checkbox" bitCheckbox formControlName="limitCollectionDeletion" />
</bit-form-control>
</ng-container>
<ng-container *ngIf="!limitCollectionCreationDeletionSplitFeatureFlagIsEnabled">
<bit-form-control>
<bit-label>{{ "limitCollectionCreationDeletionDesc" | i18n }}</bit-label>
<input type="checkbox" bitCheckbox formControlName="limitCollectionCreationDeletion" />
</bit-form-control>
</ng-container>
<button
*ngIf="!selfHosted"
*ngIf="!selfHosted || limitCollectionCreationDeletionSplitFeatureFlagIsEnabled"
type="submit"
bitButton
bitFormButton

View File

@@ -10,6 +10,8 @@ import { OrganizationCollectionManagementUpdateRequest } from "@bitwarden/common
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
import { OrganizationUpdateRequest } from "@bitwarden/common/admin-console/models/request/organization-update.request";
import { OrganizationResponse } from "@bitwarden/common/admin-console/models/response/organization.response";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -38,6 +40,8 @@ export class AccountComponent implements OnInit, OnDestroy {
org: OrganizationResponse;
taxFormPromise: Promise<unknown>;
limitCollectionCreationDeletionSplitFeatureFlagIsEnabled: boolean;
// FormGroup validators taken from server Organization domain object
protected formGroup = this.formBuilder.group({
orgName: this.formBuilder.control(
@@ -53,6 +57,7 @@ export class AccountComponent implements OnInit, OnDestroy {
),
});
// Deprecated. Delete with https://bitwarden.atlassian.net/browse/PM-10863
protected collectionManagementFormGroup = this.formBuilder.group({
limitCollectionCreationDeletion: this.formBuilder.control({ value: false, disabled: true }),
allowAdminAccessToAllCollectionItems: this.formBuilder.control({
@@ -61,6 +66,15 @@ export class AccountComponent implements OnInit, OnDestroy {
}),
});
protected collectionManagementFormGroup_VNext = this.formBuilder.group({
limitCollectionCreation: this.formBuilder.control({ value: false, disabled: false }),
limitCollectionDeletion: this.formBuilder.control({ value: false, disabled: false }),
allowAdminAccessToAllCollectionItems: this.formBuilder.control({
value: false,
disabled: false,
}),
});
protected organizationId: string;
protected publicKeyBuffer: Uint8Array;
@@ -78,11 +92,17 @@ export class AccountComponent implements OnInit, OnDestroy {
private dialogService: DialogService,
private formBuilder: FormBuilder,
private toastService: ToastService,
private configService: ConfigService,
) {}
async ngOnInit() {
this.selfHosted = this.platformUtilsService.isSelfHost();
this.configService
.getFeatureFlag$(FeatureFlag.LimitCollectionCreationDeletionSplit)
.pipe(takeUntil(this.destroy$))
.subscribe((x) => (this.limitCollectionCreationDeletionSplitFeatureFlagIsEnabled = x));
this.route.params
.pipe(
switchMap((params) => this.organizationService.get$(params.organizationId)),
@@ -104,10 +124,15 @@ export class AccountComponent implements OnInit, OnDestroy {
this.canUseApi = organization.useApi;
// Update disabled states - reactive forms prefers not using disabled attribute
if (!this.selfHosted) {
this.formGroup.get("orgName").enable();
this.collectionManagementFormGroup.get("limitCollectionCreationDeletion").enable();
this.collectionManagementFormGroup.get("allowAdminAccessToAllCollectionItems").enable();
// Disabling these fields for self hosted orgs is deprecated
// This block can be completely removed as part of
// https://bitwarden.atlassian.net/browse/PM-10863
if (!this.limitCollectionCreationDeletionSplitFeatureFlagIsEnabled) {
if (!this.selfHosted) {
this.formGroup.get("orgName").enable();
this.collectionManagementFormGroup.get("limitCollectionCreationDeletion").enable();
this.collectionManagementFormGroup.get("allowAdminAccessToAllCollectionItems").enable();
}
}
if (!this.selfHosted && this.canEditSubscription) {
@@ -125,10 +150,18 @@ export class AccountComponent implements OnInit, OnDestroy {
orgName: this.org.name,
billingEmail: this.org.billingEmail,
});
this.collectionManagementFormGroup.patchValue({
limitCollectionCreationDeletion: this.org.limitCollectionCreationDeletion,
allowAdminAccessToAllCollectionItems: this.org.allowAdminAccessToAllCollectionItems,
});
if (this.limitCollectionCreationDeletionSplitFeatureFlagIsEnabled) {
this.collectionManagementFormGroup_VNext.patchValue({
limitCollectionCreation: this.org.limitCollectionCreation,
limitCollectionDeletion: this.org.limitCollectionDeletion,
allowAdminAccessToAllCollectionItems: this.org.allowAdminAccessToAllCollectionItems,
});
} else {
this.collectionManagementFormGroup.patchValue({
limitCollectionCreationDeletion: this.org.limitCollectionCreationDeletion,
allowAdminAccessToAllCollectionItems: this.org.allowAdminAccessToAllCollectionItems,
});
}
this.loading = false;
});
@@ -177,15 +210,23 @@ export class AccountComponent implements OnInit, OnDestroy {
submitCollectionManagement = async () => {
// Early exit if self-hosted
if (this.selfHosted) {
if (this.selfHosted && !this.limitCollectionCreationDeletionSplitFeatureFlagIsEnabled) {
return;
}
const request = new OrganizationCollectionManagementUpdateRequest();
request.limitCreateDeleteOwnerAdmin =
this.collectionManagementFormGroup.value.limitCollectionCreationDeletion;
request.allowAdminAccessToAllCollectionItems =
this.collectionManagementFormGroup.value.allowAdminAccessToAllCollectionItems;
if (this.limitCollectionCreationDeletionSplitFeatureFlagIsEnabled) {
request.limitCollectionCreation =
this.collectionManagementFormGroup_VNext.value.limitCollectionCreation;
request.limitCollectionDeletion =
this.collectionManagementFormGroup_VNext.value.limitCollectionDeletion;
request.allowAdminAccessToAllCollectionItems =
this.collectionManagementFormGroup_VNext.value.allowAdminAccessToAllCollectionItems;
} else {
request.limitCreateDeleteOwnerAdmin =
this.collectionManagementFormGroup.value.limitCollectionCreationDeletion;
request.allowAdminAccessToAllCollectionItems =
this.collectionManagementFormGroup.value.allowAdminAccessToAllCollectionItems;
}
await this.organizationApiService.updateCollectionManagement(this.organizationId, request);

View File

@@ -52,6 +52,36 @@ describe("DefaultRegistrationFinishService", () => {
expect(service).not.toBeFalsy();
});
describe("getOrgNameFromOrgInvite()", () => {
let orgInvite: OrganizationInvite | null;
beforeEach(() => {
orgInvite = new OrganizationInvite();
orgInvite.organizationId = "organizationId";
orgInvite.organizationUserId = "organizationUserId";
orgInvite.token = "orgInviteToken";
orgInvite.email = "email";
});
it("returns null when the org invite is null", async () => {
acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(null);
const result = await service.getOrgNameFromOrgInvite();
expect(result).toBeNull();
expect(acceptOrgInviteService.getOrganizationInvite).toHaveBeenCalled();
});
it("returns the organization name from the organization invite when it exists", async () => {
acceptOrgInviteService.getOrganizationInvite.mockResolvedValue(orgInvite);
const result = await service.getOrgNameFromOrgInvite();
expect(result).toEqual(orgInvite.organizationName);
expect(acceptOrgInviteService.getOrganizationInvite).toHaveBeenCalled();
});
});
describe("getMasterPasswordPolicyOptsFromOrgInvite()", () => {
let orgInvite: OrganizationInvite | null;

View File

@@ -32,6 +32,15 @@ export class WebRegistrationFinishService
super(cryptoService, accountApiService);
}
override async getOrgNameFromOrgInvite(): Promise<string | null> {
const orgInvite = await this.acceptOrgInviteService.getOrganizationInvite();
if (orgInvite == null) {
return null;
}
return orgInvite.organizationName;
}
override async getMasterPasswordPolicyOptsFromOrgInvite(): Promise<MasterPasswordPolicyOptions | null> {
// If there's a deep linked org invite, use it to get the password policies
const orgInvite = await this.acceptOrgInviteService.getOrganizationInvite();

View File

@@ -71,7 +71,7 @@ export class RegisterFormComponent extends BaseRegisterComponent implements OnIn
dialogService,
toastService,
);
super.modifyRegisterRequest = async (request: RegisterRequest) => {
this.modifyRegisterRequest = async (request: RegisterRequest) => {
// Org invites are deep linked. Non-existent accounts are redirected to the register page.
// Org user id and token are included here only for validation and two factor purposes.
const orgInvite = await acceptOrgInviteService.getOrganizationInvite();

View File

@@ -25,6 +25,9 @@ import {
LockV2Component,
LockIcon,
UserLockIcon,
RegistrationUserAddIcon,
RegistrationLockAltIcon,
RegistrationExpiredLinkIcon,
} from "@bitwarden/auth/angular";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
@@ -234,6 +237,7 @@ const routes: Routes = [
path: "signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: {
pageIcon: RegistrationUserAddIcon,
pageTitle: {
key: "createAccount",
},
@@ -258,12 +262,7 @@ const routes: Routes = [
path: "finish-signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: {
pageTitle: {
key: "setAStrongPassword",
},
pageSubtitle: {
key: "finishCreatingYourAccountBySettingAPassword",
},
pageIcon: RegistrationLockAltIcon,
titleId: "setAStrongPassword",
} satisfies RouteDataProperties & AnonLayoutWrapperData,
children: [
@@ -310,6 +309,7 @@ const routes: Routes = [
path: "signup-link-expired",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: {
pageIcon: RegistrationExpiredLinkIcon,
pageTitle: {
key: "expiredLink",
},

View File

@@ -3781,6 +3781,15 @@
"joinOrganization": {
"message": "Join organization"
},
"joinOrganizationName": {
"message": "Join $ORGANIZATIONNAME$",
"placeholders": {
"organizationName": {
"content": "$1",
"example": "My Org Name"
}
}
},
"joinOrganizationDesc": {
"message": "You've been invited to join the organization listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
},
@@ -8192,6 +8201,12 @@
"limitCollectionCreationDeletionDesc": {
"message": "Limit collection creation and deletion to owners and admins"
},
"limitCollectionCreationDesc": {
"message": "Limit collection creation to owners and admins"
},
"limitCollectionDeletionDesc": {
"message": "Limit collection deletion to owners and admins"
},
"allowAdminAccessToAllCollectionItemsDesc": {
"message": "Owners and admins can manage all collections and items"
},