From 2757fcee86c18f1af74f5e14213532ff87a69e4c Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 31 Jan 2023 19:03:27 +0100 Subject: [PATCH] [SM-378] Enable SM on a user basis (#4497) * Add support for giving individual users access to secrets manager --- .../core/services/user-admin.service.ts | 3 +++ .../views/organization-user-admin-view.ts | 2 ++ .../manage/user-add-edit.component.html | 0 .../manage/user-add-edit.component.ts | 0 .../member-dialog.component.html | 21 +++++++++++++++++++ .../member-dialog/member-dialog.component.ts | 5 +++++ apps/web/src/locales/en/messages.json | 9 ++++++++ .../organization-user-invite.request.ts | 1 + .../organization-user-update.request.ts | 1 + .../responses/organization-user.response.ts | 2 ++ .../src/models/data/organization.data.ts | 2 ++ libs/common/src/models/domain/organization.ts | 4 +++- .../response/profile-organization.response.ts | 2 ++ 13 files changed, 51 insertions(+), 1 deletion(-) delete mode 100644 apps/web/src/app/organizations/manage/user-add-edit.component.html delete mode 100644 apps/web/src/app/organizations/manage/user-add-edit.component.ts diff --git a/apps/web/src/app/organizations/core/services/user-admin.service.ts b/apps/web/src/app/organizations/core/services/user-admin.service.ts index f94298d51dc..2f5c238b90f 100644 --- a/apps/web/src/app/organizations/core/services/user-admin.service.ts +++ b/apps/web/src/app/organizations/core/services/user-admin.service.ts @@ -42,6 +42,7 @@ export class UserAdminService { request.type = user.type; request.collections = user.collections; request.groups = user.groups; + request.accessSecretsManager = user.accessSecretsManager; await this.organizationUserService.putOrganizationUser(user.organizationId, user.id, request); } @@ -54,6 +55,7 @@ export class UserAdminService { request.type = user.type; request.collections = user.collections; request.groups = user.groups; + request.accessSecretsManager = user.accessSecretsManager; await this.organizationUserService.postOrganizationUserInvite(user.organizationId, request); } @@ -79,6 +81,7 @@ export class UserAdminService { readOnly: c.readOnly, })); view.groups = u.groups; + view.accessSecretsManager = u.accessSecretsManager; return view; }); diff --git a/apps/web/src/app/organizations/core/views/organization-user-admin-view.ts b/apps/web/src/app/organizations/core/views/organization-user-admin-view.ts index df5c985f2d1..3d8c83a5a31 100644 --- a/apps/web/src/app/organizations/core/views/organization-user-admin-view.ts +++ b/apps/web/src/app/organizations/core/views/organization-user-admin-view.ts @@ -16,4 +16,6 @@ export class OrganizationUserAdminView { collections: CollectionAccessSelectionView[] = []; groups: string[] = []; + + accessSecretsManager: boolean; } diff --git a/apps/web/src/app/organizations/manage/user-add-edit.component.html b/apps/web/src/app/organizations/manage/user-add-edit.component.html deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/web/src/app/organizations/manage/user-add-edit.component.ts b/apps/web/src/app/organizations/manage/user-add-edit.component.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.html b/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.html index 0e8010693c1..c7a826b5932 100644 --- a/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.html +++ b/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.html @@ -253,6 +253,27 @@ + + +

+ {{ "secretsManagerBeta" | i18n }} + + + +

+

{{ "secretsManagerBetaDesc" | i18n }}

+ + + + {{ "userAccessSecretsManager" | i18n }} + + +
diff --git a/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.ts index 06596e101dc..f52fe284a06 100644 --- a/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.ts @@ -69,6 +69,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy { organizationUserType = OrganizationUserType; canUseCustomPermissions: boolean; PermissionMode = PermissionMode; + canUseSecretsManager: boolean; protected organization: Organization; protected collectionAccessItems: AccessItemView[] = []; @@ -78,6 +79,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy { emails: ["", [Validators.required, commaSeparatedEmails]], type: OrganizationUserType.User, accessAllCollections: false, + accessSecretsManager: false, access: [[] as AccessItemValue[]], groups: [[] as AccessItemValue[]], }); @@ -158,6 +160,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy { .subscribe(({ organization, collections, userDetails, groups }) => { this.organization = organization; this.canUseCustomPermissions = organization.useCustomPermissions; + this.canUseSecretsManager = organization.useSecretsManager; this.collectionAccessItems = [].concat( collections.map((c) => mapCollectionToAccessItemView(c)) @@ -226,6 +229,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy { type: userDetails.type, accessAllCollections: userDetails.accessAll, access: accessSelections, + accessSecretsManager: userDetails.accessSecretsManager, groups: groupAccessSelections, }); } @@ -324,6 +328,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy { .filter((v) => v.type === AccessItemType.Collection) .map(convertToSelectionView); userView.groups = this.formGroup.value.groups.map((m) => m.id); + userView.accessSecretsManager = this.formGroup.value.accessSecretsManager; if (this.editMode) { await this.userService.save(userView); diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 98898b940d5..3c046cb1c5f 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -6146,5 +6146,14 @@ }, "changeKdfLoggedOutWarning": { "message": "Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour. We recommend exporting your vault before changing your encryption settings to prevent data loss." + }, + "secretsManagerBeta": { + "message": "Secrets Manager Beta" + }, + "secretsManagerBetaDesc": { + "message": "Enable user access to the Secrets Manager at no charge during the Beta program." + }, + "userAccessSecretsManager": { + "message": "This user can access the Secrets Manager Beta" } } diff --git a/libs/common/src/abstractions/organization-user/requests/organization-user-invite.request.ts b/libs/common/src/abstractions/organization-user/requests/organization-user-invite.request.ts index 66940fdbe88..ac69f2cdbce 100644 --- a/libs/common/src/abstractions/organization-user/requests/organization-user-invite.request.ts +++ b/libs/common/src/abstractions/organization-user/requests/organization-user-invite.request.ts @@ -6,6 +6,7 @@ export class OrganizationUserInviteRequest { emails: string[] = []; type: OrganizationUserType; accessAll: boolean; + accessSecretsManager: boolean; collections: SelectionReadOnlyRequest[] = []; groups: string[]; permissions: PermissionsApi; diff --git a/libs/common/src/abstractions/organization-user/requests/organization-user-update.request.ts b/libs/common/src/abstractions/organization-user/requests/organization-user-update.request.ts index 9971eda03d5..12addd7c98f 100644 --- a/libs/common/src/abstractions/organization-user/requests/organization-user-update.request.ts +++ b/libs/common/src/abstractions/organization-user/requests/organization-user-update.request.ts @@ -5,6 +5,7 @@ import { SelectionReadOnlyRequest } from "../../../models/request/selection-read export class OrganizationUserUpdateRequest { type: OrganizationUserType; accessAll: boolean; + accessSecretsManager: boolean; collections: SelectionReadOnlyRequest[] = []; groups: string[] = []; permissions: PermissionsApi; diff --git a/libs/common/src/abstractions/organization-user/responses/organization-user.response.ts b/libs/common/src/abstractions/organization-user/responses/organization-user.response.ts index 745d8e98445..f0f536ad601 100644 --- a/libs/common/src/abstractions/organization-user/responses/organization-user.response.ts +++ b/libs/common/src/abstractions/organization-user/responses/organization-user.response.ts @@ -11,6 +11,7 @@ export class OrganizationUserResponse extends BaseResponse { type: OrganizationUserType; status: OrganizationUserStatusType; accessAll: boolean; + accessSecretsManager: boolean; permissions: PermissionsApi; resetPasswordEnrolled: boolean; collections: SelectionReadOnlyResponse[] = []; @@ -24,6 +25,7 @@ export class OrganizationUserResponse extends BaseResponse { this.status = this.getResponseProperty("Status"); this.permissions = new PermissionsApi(this.getResponseProperty("Permissions")); this.accessAll = this.getResponseProperty("AccessAll"); + this.accessSecretsManager = this.getResponseProperty("AccessSecretsManager"); this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled"); const collections = this.getResponseProperty("Collections"); diff --git a/libs/common/src/models/data/organization.data.ts b/libs/common/src/models/data/organization.data.ts index feb8036ed6c..838381e4ac2 100644 --- a/libs/common/src/models/data/organization.data.ts +++ b/libs/common/src/models/data/organization.data.ts @@ -45,6 +45,7 @@ export class OrganizationData { familySponsorshipLastSyncDate?: Date; familySponsorshipValidUntil?: Date; familySponsorshipToDelete?: boolean; + accessSecretsManager: boolean; constructor(response: ProfileOrganizationResponse) { this.id = response.id; @@ -86,5 +87,6 @@ export class OrganizationData { this.familySponsorshipLastSyncDate = response.familySponsorshipLastSyncDate; this.familySponsorshipValidUntil = response.familySponsorshipValidUntil; this.familySponsorshipToDelete = response.familySponsorshipToDelete; + this.accessSecretsManager = response.accessSecretsManager; } } diff --git a/libs/common/src/models/domain/organization.ts b/libs/common/src/models/domain/organization.ts index 52d1d8887aa..e4e5e746275 100644 --- a/libs/common/src/models/domain/organization.ts +++ b/libs/common/src/models/domain/organization.ts @@ -47,6 +47,7 @@ export class Organization { familySponsorshipLastSyncDate?: Date; familySponsorshipValidUntil?: Date; familySponsorshipToDelete?: boolean; + accessSecretsManager: boolean; constructor(obj?: OrganizationData) { if (obj == null) { @@ -93,6 +94,7 @@ export class Organization { this.familySponsorshipLastSyncDate = obj.familySponsorshipLastSyncDate; this.familySponsorshipValidUntil = obj.familySponsorshipValidUntil; this.familySponsorshipToDelete = obj.familySponsorshipToDelete; + this.accessSecretsManager = obj.accessSecretsManager; } get canAccess() { @@ -199,7 +201,7 @@ export class Organization { } get canAccessSecretsManager() { - return this.useSecretsManager; + return this.useSecretsManager && this.accessSecretsManager; } static fromJSON(json: Jsonify) { diff --git a/libs/common/src/models/response/profile-organization.response.ts b/libs/common/src/models/response/profile-organization.response.ts index 562b71710e6..d12153152f8 100644 --- a/libs/common/src/models/response/profile-organization.response.ts +++ b/libs/common/src/models/response/profile-organization.response.ts @@ -46,6 +46,7 @@ export class ProfileOrganizationResponse extends BaseResponse { familySponsorshipLastSyncDate?: Date; familySponsorshipValidUntil?: Date; familySponsorshipToDelete?: boolean; + accessSecretsManager: boolean; constructor(response: any) { super(response); @@ -99,5 +100,6 @@ export class ProfileOrganizationResponse extends BaseResponse { this.familySponsorshipValidUntil = new Date(familySponsorshipValidUntilString); } this.familySponsorshipToDelete = this.getResponseProperty("FamilySponsorshipToDelete"); + this.accessSecretsManager = this.getResponseProperty("AccessSecretsManager"); } }