1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-27 04:03:00 +02:00

[SM-378] Enable SM on a user basis (#4497)

* Add support for giving individual users access to secrets manager
This commit is contained in:
Oscar Hinton 2023-01-31 19:03:27 +01:00 committed by GitHub
parent 29cc94ed70
commit 2757fcee86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 51 additions and 1 deletions

View File

@ -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;
});

View File

@ -16,4 +16,6 @@ export class OrganizationUserAdminView {
collections: CollectionAccessSelectionView[] = [];
groups: string[] = [];
accessSecretsManager: boolean;
}

View File

@ -253,6 +253,27 @@
</div>
</div>
</ng-container>
<ng-container *ngIf="canUseSecretsManager">
<h3 class="mt-4">
{{ "secretsManagerBeta" | i18n }}
<a
target="_blank"
rel="noopener"
appA11yTitle="{{ 'learnMore' | i18n }}"
href="https://bitwarden.com/help/secrets-manager"
>
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a>
</h3>
<p class="tw-text-muted">{{ "secretsManagerBetaDesc" | i18n }}</p>
<bit-form-control>
<input type="checkbox" bitCheckbox formControlName="accessSecretsManager" />
<bit-label>
{{ "userAccessSecretsManager" | i18n }}
</bit-label>
</bit-form-control>
</ng-container>
</bit-tab>
<bit-tab *ngIf="organization.useGroups" [label]="'groups' | i18n">
<div class="tw-mb-6">

View File

@ -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);

View File

@ -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"
}
}

View File

@ -6,6 +6,7 @@ export class OrganizationUserInviteRequest {
emails: string[] = [];
type: OrganizationUserType;
accessAll: boolean;
accessSecretsManager: boolean;
collections: SelectionReadOnlyRequest[] = [];
groups: string[];
permissions: PermissionsApi;

View File

@ -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;

View File

@ -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");

View File

@ -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;
}
}

View File

@ -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<Organization>) {

View File

@ -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");
}
}