diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index 798b7f7d9f..4c48322cf3 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -5561,6 +5561,15 @@
"deleteSecrets":{
"message": "Delete Secrets"
},
+ "secretProjectAssociationDescription" :{
+ "message": "Select projects that the secret will be associated with. Only organization users with access to these projects will be able to see the secret."
+ },
+ "typeOrSelectProjects" :{
+ "message": "Type or select Projects"
+ },
+ "typeOrSelectProject" :{
+ "message": "Type or select Project"
+ },
"project":{
"message": "Project"
},
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/models/view/secret.view.ts b/bitwarden_license/bit-web/src/app/secrets-manager/models/view/secret.view.ts
index 7c949c9dde..c7f132ebdf 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/models/view/secret.view.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/models/view/secret.view.ts
@@ -1,5 +1,7 @@
import { View } from "@bitwarden/common/models/view/view";
+import { SecretProjectView } from "./secret-project.view";
+
export class SecretView implements View {
id: string;
organizationId: string;
@@ -8,4 +10,5 @@ export class SecretView implements View {
note: string;
creationDate: string;
revisionDate: string;
+ projects: SecretProjectView[];
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html
index 6f88cb69c1..8c230472b7 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html
@@ -23,7 +23,43 @@
-
+
+ {{
+ "secretProjectAssociationDescription" | i18n
+ }}
+
+ {{ "project" | i18n }}
+
+
+ {{ "typeOrSelectProject" | i18n }}
+
+
+
+
+ {{ "project" | i18n }} |
+ |
+
+
+
+
+ {{ e.name }} |
+
+
+ |
+
+
+
+
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts
index 2f7a64b555..ebd50fbbd4 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts
@@ -1,11 +1,15 @@
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
+import { Subject, takeUntil } from "rxjs";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
+import { ProjectListView } from "../../models/view/project-list.view";
+import { SecretProjectView } from "../../models/view/secret-project.view";
import { SecretView } from "../../models/view/secret.view";
+import { ProjectService } from "../../projects/project.service";
import { SecretService } from "../secret.service";
export enum OperationType {
@@ -29,37 +33,87 @@ export class SecretDialogComponent implements OnInit {
name: new FormControl("", [Validators.required]),
value: new FormControl("", [Validators.required]),
notes: new FormControl(""),
+ project: new FormControl(""),
});
- protected loading = false;
+ protected loading = false;
+ projects: ProjectListView[];
+ selectedProjects: SecretProjectView[] = [];
+
+ private destroy$ = new Subject();
constructor(
public dialogRef: DialogRef,
@Inject(DIALOG_DATA) private data: SecretOperation,
private secretService: SecretService,
private i18nService: I18nService,
- private platformUtilsService: PlatformUtilsService
+ private platformUtilsService: PlatformUtilsService,
+ private projectService: ProjectService
) {}
async ngOnInit() {
+ this.projects = await this.projectService.getProjects(this.data.organizationId);
+
if (this.data.operation === OperationType.Edit && this.data.secretId) {
await this.loadData();
} else if (this.data.operation !== OperationType.Add) {
this.dialogRef.close();
throw new Error(`The secret dialog was not called with the appropriate operation values.`);
}
+
+ this.formGroup
+ .get("project")
+ .valueChanges.pipe(takeUntil(this.destroy$))
+ .subscribe(() => this.updateProjectList());
}
async loadData() {
this.loading = true;
const secret: SecretView = await this.secretService.getBySecretId(this.data.secretId);
this.loading = false;
- this.formGroup.setValue({ name: secret.name, value: secret.value, notes: secret.note });
+ this.selectedProjects = secret.projects;
+ this.loading = false;
+ this.formGroup.setValue({
+ name: secret.name,
+ value: secret.value,
+ notes: secret.note,
+ project: "",
+ });
+ }
+
+ ngOnDestroy(): void {
+ this.destroy$.next();
+ this.destroy$.complete();
}
get title() {
return this.data.operation === OperationType.Add ? "newSecret" : "editSecret";
}
+ async removeProjectAssociation(id: string) {
+ this.selectedProjects = this.selectedProjects.filter((e) => e.id != id);
+ this.formGroup.get("project").setValue("");
+ }
+
+ updateProjectList() {
+ const newList: SecretProjectView[] = [];
+ const projectId = this.formGroup.get("project").value;
+
+ if (projectId) {
+ const selectedProject = this.projects?.filter((p) => p.id == projectId)[0];
+
+ if (selectedProject != undefined) {
+ const projectSecretView = new SecretProjectView();
+
+ projectSecretView.id = selectedProject.id;
+ projectSecretView.name = selectedProject.name;
+
+ newList.push(projectSecretView);
+ }
+ }
+
+ this.selectedProjects = newList;
+ }
+
submit = async () => {
this.formGroup.markAllAsTouched();
@@ -69,7 +123,7 @@ export class SecretDialogComponent implements OnInit {
const secretView = this.getSecretView();
if (this.data.operation === OperationType.Add) {
- await this.createSecret(secretView, this.data.projectId);
+ await this.createSecret(secretView);
} else {
secretView.id = this.data.secretId;
await this.updateSecret(secretView);
@@ -77,8 +131,8 @@ export class SecretDialogComponent implements OnInit {
this.dialogRef.close();
};
- private async createSecret(secretView: SecretView, projectId?: string) {
- await this.secretService.create(this.data.organizationId, secretView, projectId);
+ private async createSecret(secretView: SecretView) {
+ await this.secretService.create(this.data.organizationId, secretView);
this.platformUtilsService.showToast("success", null, this.i18nService.t("secretCreated"));
}
@@ -88,11 +142,14 @@ export class SecretDialogComponent implements OnInit {
}
private getSecretView() {
+ const emptyProjects: SecretProjectView[] = [];
+
const secretView = new SecretView();
secretView.organizationId = this.data.organizationId;
secretView.name = this.formGroup.value.name;
secretView.value = this.formGroup.value.value;
secretView.note = this.formGroup.value.notes;
+ secretView.projects = this.selectedProjects ? this.selectedProjects : emptyProjects;
return secretView;
}
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/requests/secret.request.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/requests/secret.request.ts
index d84641341d..9fdd7567f1 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/requests/secret.request.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/requests/secret.request.ts
@@ -2,5 +2,5 @@ export class SecretRequest {
key: string;
value: string;
note: string;
- projectId?: string;
+ projectIds?: string[];
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/responses/secret-list-item.response.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/responses/secret-list-item.response.ts
index 3819f95870..bfac1dfe75 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/responses/secret-list-item.response.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/responses/secret-list-item.response.ts
@@ -1,12 +1,14 @@
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
+import { SecretProjectResponse } from "./secret-project.response";
+
export class SecretListItemResponse extends BaseResponse {
id: string;
organizationId: string;
name: string;
creationDate: string;
revisionDate: string;
- projects: string[];
+ projects: SecretProjectResponse[];
constructor(response: any) {
super(response);
@@ -15,6 +17,8 @@ export class SecretListItemResponse extends BaseResponse {
this.name = this.getResponseProperty("Key");
this.creationDate = this.getResponseProperty("CreationDate");
this.revisionDate = this.getResponseProperty("RevisionDate");
- this.projects = this.getResponseProperty("projects");
+
+ const project = this.getResponseProperty("projects");
+ this.projects = project == null ? null : project.map((k: any) => new SecretProjectResponse(k));
}
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/responses/secret.response.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/responses/secret.response.ts
index c873ac6206..9296c3d869 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/responses/secret.response.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/responses/secret.response.ts
@@ -1,5 +1,7 @@
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
+import { SecretProjectResponse } from "./secret-project.response";
+
export class SecretResponse extends BaseResponse {
id: string;
organizationId: string;
@@ -8,6 +10,7 @@ export class SecretResponse extends BaseResponse {
note: string;
creationDate: string;
revisionDate: string;
+ projects: SecretProjectResponse[];
constructor(response: any) {
super(response);
@@ -18,5 +21,9 @@ export class SecretResponse extends BaseResponse {
this.note = this.getResponseProperty("Note");
this.creationDate = this.getResponseProperty("CreationDate");
this.revisionDate = this.getResponseProperty("RevisionDate");
+
+ const projects = this.getResponseProperty("Projects");
+ this.projects =
+ projects == null ? null : projects.map((k: any) => new SecretProjectResponse(k));
}
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts
index 1b87652ee6..9e7e1d5fbe 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts
@@ -34,6 +34,7 @@ export class SecretService {
async getBySecretId(secretId: string): Promise {
const r = await this.apiService.send("GET", "/secrets/" + secretId, null, true, true);
const secretResponse = new SecretResponse(r);
+
return await this.createSecretView(secretResponse);
}
@@ -63,8 +64,8 @@ export class SecretService {
return await this.createSecretsListView(organizationId, results);
}
- async create(organizationId: string, secretView: SecretView, projectId?: string) {
- const request = await this.getSecretRequest(organizationId, secretView, projectId);
+ async create(organizationId: string, secretView: SecretView) {
+ const request = await this.getSecretRequest(organizationId, secretView);
const r = await this.apiService.send(
"POST",
"/organizations/" + organizationId + "/secrets",
@@ -106,8 +107,7 @@ export class SecretService {
private async getSecretRequest(
organizationId: string,
- secretView: SecretView,
- projectId?: string
+ secretView: SecretView
): Promise {
const orgKey = await this.getOrganizationKey(organizationId);
const request = new SecretRequest();
@@ -119,7 +119,10 @@ export class SecretService {
request.key = key.encryptedString;
request.value = value.encryptedString;
request.note = note.encryptedString;
- request.projectId = projectId;
+ request.projectIds = [];
+
+ secretView.projects?.forEach((e) => request.projectIds.push(e.id));
+
return request;
}
@@ -141,6 +144,13 @@ export class SecretService {
secretView.value = value;
secretView.note = note;
+ if (secretResponse.projects != null) {
+ secretView.projects = await this.decryptProjectsMappedToSecrets(
+ orgKey,
+ secretResponse.projects
+ );
+ }
+
return secretView;
}
@@ -150,7 +160,7 @@ export class SecretService {
): Promise {
const orgKey = await this.getOrganizationKey(organizationId);
- const projectsMappedToSecretsView = this.decryptProjectsMappedToSecrets(
+ const projectsMappedToSecretsView = await this.decryptProjectsMappedToSecrets(
orgKey,
secrets.projects
);
@@ -166,9 +176,12 @@ export class SecretService {
);
secretListView.creationDate = s.creationDate;
secretListView.revisionDate = s.revisionDate;
- secretListView.projects = (await projectsMappedToSecretsView).filter((p) =>
- s.projects.includes(p.id)
+
+ const projectIds = s.projects?.map((p) => p.id);
+ secretListView.projects = projectsMappedToSecretsView.filter((p) =>
+ projectIds.includes(p.id)
);
+
return secretListView;
})
);
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html
index 33f6298ec7..46a0502145 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html
@@ -26,7 +26,7 @@
{{ "name" | i18n }} |
- {{ "projects" | i18n }} |
+ {{ "project" | i18n }} |
{{ "lastEdited" | i18n }} |
|