1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[AC-1117] Add manage permission (#5910)

* Add 'manage' option to collection access permissions

* Add 'manage' to collection permissions

* remove service accidentally committed from another branch

* Update CLI commands

* update message casing to be consistent

* access selector model updates
This commit is contained in:
Robyn MacCallum
2023-08-11 14:51:46 -04:00
committed by GitHub
parent a05b4fd094
commit c3bcd732cf
15 changed files with 45 additions and 13 deletions

View File

@@ -1,15 +1,17 @@
export class SelectionReadOnly { export class SelectionReadOnly {
static template(): SelectionReadOnly { static template(): SelectionReadOnly {
return new SelectionReadOnly("00000000-0000-0000-0000-000000000000", false, false); return new SelectionReadOnly("00000000-0000-0000-0000-000000000000", false, false, false);
} }
id: string; id: string;
readOnly: boolean; readOnly: boolean;
hidePasswords: boolean; hidePasswords: boolean;
manage: boolean;
constructor(id: string, readOnly: boolean, hidePasswords: boolean) { constructor(id: string, readOnly: boolean, hidePasswords: boolean, manage: boolean) {
this.id = id; this.id = id;
this.readOnly = readOnly; this.readOnly = readOnly;
this.hidePasswords = hidePasswords || false; this.hidePasswords = hidePasswords || false;
this.manage = manage;
} }
} }

View File

@@ -163,7 +163,9 @@ export class EditCommand {
const groups = const groups =
req.groups == null req.groups == null
? null ? null
: req.groups.map((g) => new SelectionReadOnlyRequest(g.id, g.readOnly, g.hidePasswords)); : req.groups.map(
(g) => new SelectionReadOnlyRequest(g.id, g.readOnly, g.hidePasswords, g.manage)
);
const request = new CollectionRequest(); const request = new CollectionRequest();
request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString; request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString;
request.externalId = req.externalId; request.externalId = req.externalId;

View File

@@ -425,7 +425,9 @@ export class GetCommand extends DownloadCommand {
const groups = const groups =
response.groups == null response.groups == null
? null ? null
: response.groups.map((g) => new SelectionReadOnly(g.id, g.readOnly, g.hidePasswords)); : response.groups.map(
(g) => new SelectionReadOnly(g.id, g.readOnly, g.hidePasswords, g.manage)
);
const res = new OrganizationCollectionResponse(decCollection, groups); const res = new OrganizationCollectionResponse(decCollection, groups);
return Response.success(res); return Response.success(res);
} catch (e) { } catch (e) {

View File

@@ -180,7 +180,9 @@ export class CreateCommand {
const groups = const groups =
req.groups == null req.groups == null
? null ? null
: req.groups.map((g) => new SelectionReadOnlyRequest(g.id, g.readOnly, g.hidePasswords)); : req.groups.map(
(g) => new SelectionReadOnlyRequest(g.id, g.readOnly, g.hidePasswords, g.manage)
);
const request = new CollectionRequest(); const request = new CollectionRequest();
request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString; request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString;
request.externalId = req.externalId; request.externalId = req.externalId;

View File

@@ -76,7 +76,7 @@ export class InternalGroupService extends GroupService {
request.accessAll = group.accessAll; request.accessAll = group.accessAll;
request.users = group.members; request.users = group.members;
request.collections = group.collections.map( request.collections = group.collections.map(
(c) => new SelectionReadOnlyRequest(c.id, c.readOnly, c.hidePasswords) (c) => new SelectionReadOnlyRequest(c.id, c.readOnly, c.hidePasswords, c.manage)
); );
if (group.id == undefined) { if (group.id == undefined) {

View File

@@ -80,6 +80,7 @@ export class UserAdminService {
id: c.id, id: c.id,
hidePasswords: c.hidePasswords, hidePasswords: c.hidePasswords,
readOnly: c.readOnly, readOnly: c.readOnly,
manage: c.manage,
})); }));
view.groups = u.groups; view.groups = u.groups;
view.accessSecretsManager = u.accessSecretsManager; view.accessSecretsManager = u.accessSecretsManager;

View File

@@ -4,12 +4,14 @@ interface SelectionResponseLike {
id: string; id: string;
readOnly: boolean; readOnly: boolean;
hidePasswords: boolean; hidePasswords: boolean;
manage: boolean;
} }
export class CollectionAccessSelectionView extends View { export class CollectionAccessSelectionView extends View {
readonly id: string; readonly id: string;
readonly readOnly: boolean; readonly readOnly: boolean;
readonly hidePasswords: boolean; readonly hidePasswords: boolean;
readonly manage: boolean;
constructor(response?: SelectionResponseLike) { constructor(response?: SelectionResponseLike) {
super(); super();
@@ -21,5 +23,6 @@ export class CollectionAccessSelectionView extends View {
this.id = response.id; this.id = response.id;
this.readOnly = response.readOnly; this.readOnly = response.readOnly;
this.hidePasswords = response.hidePasswords; this.hidePasswords = response.hidePasswords;
this.manage = response.manage;
} }
} }

View File

@@ -142,7 +142,12 @@ export class EntityUsersComponent implements OnInit {
.filter((u) => (u as any).checked && !u.accessAll) .filter((u) => (u as any).checked && !u.accessAll)
.map( .map(
(u) => (u) =>
new SelectionReadOnlyRequest(u.id, !!(u as any).readOnly, !!(u as any).hidePasswords) new SelectionReadOnlyRequest(
u.id,
!!(u as any).readOnly,
!!(u as any).hidePasswords,
!!(u as any).manage
)
); );
this.formPromise = this.apiService.putCollectionUsers( this.formPromise = this.apiService.putCollectionUsers(
this.organizationId, this.organizationId,

View File

@@ -121,6 +121,7 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On
{ perm: CollectionPermission.ViewExceptPass, labelId: "canViewExceptPass" }, { perm: CollectionPermission.ViewExceptPass, labelId: "canViewExceptPass" },
{ perm: CollectionPermission.Edit, labelId: "canEdit" }, { perm: CollectionPermission.Edit, labelId: "canEdit" },
{ perm: CollectionPermission.EditExceptPass, labelId: "canEditExceptPass" }, { perm: CollectionPermission.EditExceptPass, labelId: "canEditExceptPass" },
{ perm: CollectionPermission.Manage, labelId: "canManage" },
]; ];
protected initialPermission = CollectionPermission.View; protected initialPermission = CollectionPermission.View;

View File

@@ -7,13 +7,14 @@ import { SelectItemView } from "@bitwarden/components";
import { CollectionAccessSelectionView } from "../../../core"; import { CollectionAccessSelectionView } from "../../../core";
/** /**
* Permission options that replace/correspond with readOnly and hidePassword server fields. * Permission options that replace/correspond with manage, readOnly, and hidePassword server fields.
*/ */
export enum CollectionPermission { export enum CollectionPermission {
View = "view", View = "view",
ViewExceptPass = "viewExceptPass", ViewExceptPass = "viewExceptPass",
Edit = "edit", Edit = "edit",
EditExceptPass = "editExceptPass", EditExceptPass = "editExceptPass",
Manage = "manage",
} }
export enum AccessItemType { export enum AccessItemType {
@@ -82,7 +83,9 @@ export type AccessItemValue = {
* @param value * @param value
*/ */
export const convertToPermission = (value: CollectionAccessSelectionView) => { export const convertToPermission = (value: CollectionAccessSelectionView) => {
if (value.readOnly) { if (value.manage) {
return CollectionPermission.Manage;
} else if (value.readOnly) {
return value.hidePasswords ? CollectionPermission.ViewExceptPass : CollectionPermission.View; return value.hidePasswords ? CollectionPermission.ViewExceptPass : CollectionPermission.View;
} else { } else {
return value.hidePasswords ? CollectionPermission.EditExceptPass : CollectionPermission.Edit; return value.hidePasswords ? CollectionPermission.EditExceptPass : CollectionPermission.Edit;
@@ -91,7 +94,7 @@ export const convertToPermission = (value: CollectionAccessSelectionView) => {
/** /**
* Converts an AccessItemValue back into a CollectionAccessView class using the CollectionPermission * Converts an AccessItemValue back into a CollectionAccessView class using the CollectionPermission
* to determine the values for `readOnly` and `hidePassword` * to determine the values for `manage`, `readOnly`, and `hidePassword`
* @param value * @param value
*/ */
export const convertToSelectionView = (value: AccessItemValue) => { export const convertToSelectionView = (value: AccessItemValue) => {
@@ -99,6 +102,7 @@ export const convertToSelectionView = (value: AccessItemValue) => {
id: value.id, id: value.id,
readOnly: readOnly(value.permission), readOnly: readOnly(value.permission),
hidePasswords: hidePassword(value.permission), hidePasswords: hidePassword(value.permission),
manage: value.permission === CollectionPermission.Manage,
}); });
}; };

View File

@@ -298,6 +298,7 @@ function createCollectionView(i: number): CollectionAdminView {
id: group.id, id: group.id,
hidePasswords: false, hidePasswords: false,
readOnly: false, readOnly: false,
manage: false,
}), }),
]; ];
} }

View File

@@ -105,10 +105,12 @@ export class CollectionAdminService {
collection.externalId = model.externalId; collection.externalId = model.externalId;
collection.name = (await this.cryptoService.encrypt(model.name, key)).encryptedString; collection.name = (await this.cryptoService.encrypt(model.name, key)).encryptedString;
collection.groups = model.groups.map( collection.groups = model.groups.map(
(group) => new SelectionReadOnlyRequest(group.id, group.readOnly, group.hidePasswords) (group) =>
new SelectionReadOnlyRequest(group.id, group.readOnly, group.hidePasswords, group.manage)
); );
collection.users = model.users.map( collection.users = model.users.map(
(user) => new SelectionReadOnlyRequest(user.id, user.readOnly, user.hidePasswords) (user) =>
new SelectionReadOnlyRequest(user.id, user.readOnly, user.hidePasswords, user.manage)
); );
return collection; return collection;
} }

View File

@@ -1479,6 +1479,9 @@
"manage": { "manage": {
"message": "Manage" "message": "Manage"
}, },
"canManage": {
"message": "Can manage"
},
"disable": { "disable": {
"message": "Turn off" "message": "Turn off"
}, },

View File

@@ -2,10 +2,12 @@ export class SelectionReadOnlyRequest {
id: string; id: string;
readOnly: boolean; readOnly: boolean;
hidePasswords: boolean; hidePasswords: boolean;
manage: boolean;
constructor(id: string, readOnly: boolean, hidePasswords: boolean) { constructor(id: string, readOnly: boolean, hidePasswords: boolean, manage: boolean) {
this.id = id; this.id = id;
this.readOnly = readOnly; this.readOnly = readOnly;
this.hidePasswords = hidePasswords; this.hidePasswords = hidePasswords;
this.manage = manage;
} }
} }

View File

@@ -4,11 +4,13 @@ export class SelectionReadOnlyResponse extends BaseResponse {
id: string; id: string;
readOnly: boolean; readOnly: boolean;
hidePasswords: boolean; hidePasswords: boolean;
manage: boolean;
constructor(response: any) { constructor(response: any) {
super(response); super(response);
this.id = this.getResponseProperty("Id"); this.id = this.getResponseProperty("Id");
this.readOnly = this.getResponseProperty("ReadOnly"); this.readOnly = this.getResponseProperty("ReadOnly");
this.hidePasswords = this.getResponseProperty("HidePasswords"); this.hidePasswords = this.getResponseProperty("HidePasswords");
this.manage = this.getResponseProperty("Manage");
} }
} }