mirror of
https://github.com/bitwarden/browser.git
synced 2024-09-27 04:03:00 +02:00
[EC-342] Gate custom permissions behind enterprise plan (#3907)
* [EC-342] Add 'UseCustomPermissions' property in Organization. * [EC-342] Add/Edit message texts for Permission types * [EC-342] Add check to determine if org can have custom permissions * [EC-342] Add description to message text * [EC-342] Checking if the selected user type is 'Custom' * Update apps/web/src/locales/en/messages.json Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * [EC-342] Update custom permissions check to only look for UseCustomPermissions flag. Create updateUser and inviteUser methods. * [EC-342] Split Custom Permissions text into 3 parts. Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
parent
9ec1750727
commit
d240d96368
@ -122,10 +122,22 @@
|
||||
id="userTypeCustom"
|
||||
[value]="organizationUserType.Custom"
|
||||
[(ngModel)]="type"
|
||||
[attr.disabled]="!canUseCustomPermissions || null"
|
||||
/>
|
||||
<label class="form-check-label" for="userTypeCustom">
|
||||
{{ "custom" | i18n }}
|
||||
<small>{{ "customDesc" | i18n }}</small>
|
||||
<ng-container *ngIf="!canUseCustomPermissions; else enterprise">
|
||||
<small
|
||||
>{{ "customDescNonEnterpriseStart" | i18n
|
||||
}}<a href="https://bitwarden.com/contact/" target="_blank">{{
|
||||
"customDescNonEnterpriseLink" | i18n
|
||||
}}</a
|
||||
>{{ "customDescNonEnterpriseEnd" | i18n }}</small
|
||||
>
|
||||
</ng-container>
|
||||
<ng-template #enterprise>
|
||||
<small>{{ "customDesc" | i18n }}</small>
|
||||
</ng-template>
|
||||
</label>
|
||||
</div>
|
||||
<ng-container *ngIf="customUserTypeSelected">
|
||||
|
@ -4,6 +4,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { OrganizationUserStatusType } from "@bitwarden/common/enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "@bitwarden/common/enums/organizationUserType";
|
||||
@ -43,6 +44,7 @@ export class UserAddEditComponent implements OnInit {
|
||||
formPromise: Promise<any>;
|
||||
deletePromise: Promise<any>;
|
||||
organizationUserType = OrganizationUserType;
|
||||
canUseCustomPermissions: boolean;
|
||||
|
||||
manageAllCollectionsCheckboxes = [
|
||||
{
|
||||
@ -84,11 +86,14 @@ export class UserAddEditComponent implements OnInit {
|
||||
private i18nService: I18nService,
|
||||
private collectionService: CollectionService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private organizationService: OrganizationService,
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.editMode = this.loading = this.organizationUserId != null;
|
||||
const organization = this.organizationService.get(this.organizationId);
|
||||
this.canUseCustomPermissions = organization.useCustomPermissions;
|
||||
await this.loadCollections();
|
||||
|
||||
if (this.editMode) {
|
||||
@ -163,6 +168,15 @@ export class UserAddEditComponent implements OnInit {
|
||||
}
|
||||
|
||||
async submit() {
|
||||
if (!this.canUseCustomPermissions && this.type === OrganizationUserType.Custom) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("customNonEnterpriseError")
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let collections: SelectionReadOnlyRequest[] = null;
|
||||
if (this.access !== "all") {
|
||||
collections = this.collections
|
||||
@ -172,30 +186,9 @@ export class UserAddEditComponent implements OnInit {
|
||||
|
||||
try {
|
||||
if (this.editMode) {
|
||||
const request = new OrganizationUserUpdateRequest();
|
||||
request.accessAll = this.access === "all";
|
||||
request.type = this.type;
|
||||
request.collections = collections;
|
||||
request.permissions = this.setRequestPermissions(
|
||||
request.permissions ?? new PermissionsApi(),
|
||||
request.type !== OrganizationUserType.Custom
|
||||
);
|
||||
this.formPromise = this.apiService.putOrganizationUser(
|
||||
this.organizationId,
|
||||
this.organizationUserId,
|
||||
request
|
||||
);
|
||||
this.updateUser(collections);
|
||||
} else {
|
||||
const request = new OrganizationUserInviteRequest();
|
||||
request.emails = [...new Set(this.emails.trim().split(/\s*,\s*/))];
|
||||
request.accessAll = this.access === "all";
|
||||
request.type = this.type;
|
||||
request.permissions = this.setRequestPermissions(
|
||||
request.permissions ?? new PermissionsApi(),
|
||||
request.type !== OrganizationUserType.Custom
|
||||
);
|
||||
request.collections = collections;
|
||||
this.formPromise = this.apiService.postOrganizationUserInvite(this.organizationId, request);
|
||||
this.inviteUser(collections);
|
||||
}
|
||||
await this.formPromise;
|
||||
this.platformUtilsService.showToast(
|
||||
@ -301,4 +294,33 @@ export class UserAddEditComponent implements OnInit {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
updateUser(collections: SelectionReadOnlyRequest[]) {
|
||||
const request = new OrganizationUserUpdateRequest();
|
||||
request.accessAll = this.access === "all";
|
||||
request.type = this.type;
|
||||
request.collections = collections;
|
||||
request.permissions = this.setRequestPermissions(
|
||||
request.permissions ?? new PermissionsApi(),
|
||||
request.type !== OrganizationUserType.Custom
|
||||
);
|
||||
this.formPromise = this.apiService.putOrganizationUser(
|
||||
this.organizationId,
|
||||
this.organizationUserId,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
inviteUser(collections: SelectionReadOnlyRequest[]) {
|
||||
const request = new OrganizationUserInviteRequest();
|
||||
request.emails = [...new Set(this.emails.trim().split(/\s*,\s*/))];
|
||||
request.accessAll = this.access === "all";
|
||||
request.type = this.type;
|
||||
request.permissions = this.setRequestPermissions(
|
||||
request.permissions ?? new PermissionsApi(),
|
||||
request.type !== OrganizationUserType.Custom
|
||||
);
|
||||
request.collections = collections;
|
||||
this.formPromise = this.apiService.postOrganizationUserInvite(this.organizationId, request);
|
||||
}
|
||||
}
|
||||
|
@ -2444,7 +2444,7 @@
|
||||
"message": "Owner"
|
||||
},
|
||||
"ownerDesc": {
|
||||
"message": "The highest access user that can manage all aspects of your organization."
|
||||
"message": "Manage all aspects of your organization, including billing and subscriptions"
|
||||
},
|
||||
"clientOwnerDesc": {
|
||||
"message": "This user should be independent of the Provider. If the Provider is disassociated with the organization, this user will maintain ownership of the organization."
|
||||
@ -2453,19 +2453,19 @@
|
||||
"message": "Admin"
|
||||
},
|
||||
"adminDesc": {
|
||||
"message": "Admins can access and manage all items, collections and users in your organization."
|
||||
"message": "Manage organization access, all collections, members, reporting, and security settings"
|
||||
},
|
||||
"user": {
|
||||
"message": "User"
|
||||
},
|
||||
"userDesc": {
|
||||
"message": "A regular user with access to assigned collections in your organization."
|
||||
"message": "Access and add items to assigned collections"
|
||||
},
|
||||
"manager": {
|
||||
"message": "Manager"
|
||||
},
|
||||
"managerDesc": {
|
||||
"message": "Managers can access and manage assigned collections in your organization."
|
||||
"message": "Create, delete, and manage access in assigned collections"
|
||||
},
|
||||
"all": {
|
||||
"message": "All"
|
||||
@ -4117,7 +4117,22 @@
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
"message": "Grant customized permissions to members"
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Custom roles is an ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "enterprise feature",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Contact our support team to upgrade your subscription",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "To enable custom permissions the organization must be on an Enterprise 2020 plan."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
|
@ -20,6 +20,7 @@ export class OrganizationData {
|
||||
useSso: boolean;
|
||||
useKeyConnector: boolean;
|
||||
useScim: boolean;
|
||||
useCustomPermissions: boolean;
|
||||
useResetPassword: boolean;
|
||||
selfHost: boolean;
|
||||
usersGetPremium: boolean;
|
||||
@ -60,6 +61,7 @@ export class OrganizationData {
|
||||
this.useSso = response.useSso;
|
||||
this.useKeyConnector = response.useKeyConnector;
|
||||
this.useScim = response.useScim;
|
||||
this.useCustomPermissions = response.useCustomPermissions;
|
||||
this.useResetPassword = response.useResetPassword;
|
||||
this.selfHost = response.selfHost;
|
||||
this.usersGetPremium = response.usersGetPremium;
|
||||
|
@ -22,6 +22,7 @@ export class Organization {
|
||||
useSso: boolean;
|
||||
useKeyConnector: boolean;
|
||||
useScim: boolean;
|
||||
useCustomPermissions: boolean;
|
||||
useResetPassword: boolean;
|
||||
selfHost: boolean;
|
||||
usersGetPremium: boolean;
|
||||
@ -66,6 +67,7 @@ export class Organization {
|
||||
this.useSso = obj.useSso;
|
||||
this.useKeyConnector = obj.useKeyConnector;
|
||||
this.useScim = obj.useScim;
|
||||
this.useCustomPermissions = obj.useCustomPermissions;
|
||||
this.useResetPassword = obj.useResetPassword;
|
||||
this.selfHost = obj.selfHost;
|
||||
this.usersGetPremium = obj.usersGetPremium;
|
||||
|
@ -18,6 +18,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
||||
useSso: boolean;
|
||||
useKeyConnector: boolean;
|
||||
useScim: boolean;
|
||||
useCustomPermissions: boolean;
|
||||
useResetPassword: boolean;
|
||||
selfHost: boolean;
|
||||
usersGetPremium: boolean;
|
||||
@ -59,6 +60,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
||||
this.useSso = this.getResponseProperty("UseSso");
|
||||
this.useKeyConnector = this.getResponseProperty("UseKeyConnector") ?? false;
|
||||
this.useScim = this.getResponseProperty("UseScim") ?? false;
|
||||
this.useCustomPermissions = this.getResponseProperty("UseCustomPermissions") ?? false;
|
||||
this.useResetPassword = this.getResponseProperty("UseResetPassword");
|
||||
this.selfHost = this.getResponseProperty("SelfHost");
|
||||
this.usersGetPremium = this.getResponseProperty("UsersGetPremium");
|
||||
|
Loading…
Reference in New Issue
Block a user