1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-22 16:29:09 +01:00

[SM-579] Prevent creating secret without project (#4892)

* Change project select to simple dropdown

* Handle null
This commit is contained in:
Oscar Hinton 2023-03-07 10:14:14 +01:00 committed by GitHub
parent 9fe79afa13
commit f2276227aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 100 deletions

View File

@ -1,14 +1,11 @@
<!-- Please remove this disable statement when editing this file! -->
<!-- eslint-disable tailwindcss/no-custom-classname -->
<form [formGroup]="formGroup" [bitSubmit]="submit"> <form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-dialog dialogSize="default" disablePadding> <bit-dialog dialogSize="default">
<ng-container bitDialogTitle>{{ title | i18n }}</ng-container> <ng-container bitDialogTitle>{{ title | i18n }}</ng-container>
<div bitDialogContent> <div bitDialogContent>
<div *ngIf="loading" class="tw-text-center"> <div *ngIf="loading" class="tw-text-center">
<i class="bwi bwi-spinner bwi-spin bwi-3x"></i> <i class="bwi bwi-spinner bwi-spin bwi-3x"></i>
</div> </div>
<bit-tab-group *ngIf="!loading"> <ng-container *ngIf="!loading">
<bit-tab [label]="'nameValuePair' | i18n">
<div class="tw-flex tw-gap-4 tw-pt-4"> <div class="tw-flex tw-gap-4 tw-pt-4">
<bit-form-field class="tw-w-1/3"> <bit-form-field class="tw-w-1/3">
<bit-label for="secret-name">{{ "name" | i18n }}</bit-label> <bit-label for="secret-name">{{ "name" | i18n }}</bit-label>
@ -23,49 +20,19 @@
<bit-label>{{ "notes" | i18n }}</bit-label> <bit-label>{{ "notes" | i18n }}</bit-label>
<textarea bitInput rows="4" formControlName="notes"></textarea> <textarea bitInput rows="4" formControlName="notes"></textarea>
</bit-form-field> </bit-form-field>
</bit-tab>
<bit-tab [label]="'projects' | i18n"> <hr />
<bit-label class="tw-text-md">{{
"secretProjectAssociationDescription" | i18n
}}</bit-label>
<bit-form-field class="tw-mt-3 tw-mb-0"> <bit-form-field class="tw-mt-3 tw-mb-0">
<bit-label>{{ "project" | i18n }}</bit-label> <bit-label>{{ "project" | i18n }}</bit-label>
<select bitInput name="project" formControlName="project"> <select bitInput name="project" formControlName="project">
<option value="">{{ "selectPlaceholder" | i18n }}</option> <option value="">{{ "selectPlaceholder" | i18n }}</option>
<option *ngFor="let f of projects" [value]="f.id" (change)="updateProjectList()"> <option *ngFor="let f of projects" [value]="f.id">
{{ f.name }} {{ f.name }}
</option> </option>
</select> </select>
</bit-form-field> </bit-form-field>
<small class="form-text text-muted tw-mb-6">{{ "selectProjects" | i18n }}</small>
<bit-table>
<ng-container header>
<tr>
<th bitCell>{{ "project" | i18n }}</th>
<th bitCell></th>
</tr>
</ng-container> </ng-container>
<ng-template body *ngIf="selectedProjects != null">
<tr bitRow *ngFor="let e of selectedProjects">
<td bitCell class="tw-overflow-hidden tw-break-words tw-text-sm">
{{ e.name }}
</td>
<td bitCell class="tw-w-0">
<button
type="button"
(click)="removeProjectAssociation(e.id)"
bitIconButton="bwi-close"
buttonType="main"
[title]="'options' | i18n"
[attr.aria-label]="'options' | i18n"
></button>
</td>
</tr>
</ng-template>
</bit-table>
</bit-tab>
</bit-tab-group>
</div> </div>
<div bitDialogFooter class="tw-flex tw-gap-2"> <div bitDialogFooter class="tw-flex tw-gap-2">
<button type="submit" bitButton buttonType="primary" bitFormButton> <button type="submit" bitButton buttonType="primary" bitFormButton>

View File

@ -1,14 +1,13 @@
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core"; import { Component, Inject, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms"; import { FormControl, FormGroup, Validators } from "@angular/forms";
import { lastValueFrom, Subject, takeUntil } from "rxjs"; import { lastValueFrom, Subject } from "rxjs";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { DialogService } from "@bitwarden/components"; import { DialogService } from "@bitwarden/components";
import { ProjectListView } from "../../models/view/project-list.view"; import { ProjectListView } from "../../models/view/project-list.view";
import { SecretProjectView } from "../../models/view/secret-project.view";
import { SecretView } from "../../models/view/secret.view"; import { SecretView } from "../../models/view/secret.view";
import { ProjectService } from "../../projects/project.service"; import { ProjectService } from "../../projects/project.service";
import { SecretService } from "../secret.service"; import { SecretService } from "../secret.service";
@ -36,12 +35,11 @@ export class SecretDialogComponent implements OnInit {
name: new FormControl("", [Validators.required]), name: new FormControl("", [Validators.required]),
value: new FormControl("", [Validators.required]), value: new FormControl("", [Validators.required]),
notes: new FormControl(""), notes: new FormControl(""),
project: new FormControl(""), project: new FormControl("", [Validators.required]),
}); });
protected loading = false; protected loading = false;
projects: ProjectListView[]; projects: ProjectListView[];
selectedProjects: SecretProjectView[] = [];
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
constructor( constructor(
@ -66,11 +64,6 @@ export class SecretDialogComponent implements OnInit {
throw new Error(`The secret dialog was not called with the appropriate operation values.`); 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());
if (this.data.projectId) { if (this.data.projectId) {
this.formGroup.get("project").setValue(this.data.projectId); this.formGroup.get("project").setValue(this.data.projectId);
} }
@ -79,15 +72,13 @@ export class SecretDialogComponent implements OnInit {
async loadData() { async loadData() {
this.loading = true; this.loading = true;
const secret: SecretView = await this.secretService.getBySecretId(this.data.secretId); const secret: SecretView = await this.secretService.getBySecretId(this.data.secretId);
this.loading = false;
this.selectedProjects = secret.projects;
this.loading = false;
this.formGroup.setValue({ this.formGroup.setValue({
name: secret.name, name: secret.name,
value: secret.value, value: secret.value,
notes: secret.note, notes: secret.note,
project: "", project: secret.projects[0]?.id,
}); });
this.loading = false;
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -99,31 +90,6 @@ export class SecretDialogComponent implements OnInit {
return this.data.operation === OperationType.Add ? "newSecret" : "editSecret"; 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 () => { submit = async () => {
this.formGroup.markAllAsTouched(); this.formGroup.markAllAsTouched();
@ -172,14 +138,12 @@ export class SecretDialogComponent implements OnInit {
} }
private getSecretView() { private getSecretView() {
const emptyProjects: SecretProjectView[] = [];
const secretView = new SecretView(); const secretView = new SecretView();
secretView.organizationId = this.data.organizationId; secretView.organizationId = this.data.organizationId;
secretView.name = this.formGroup.value.name; secretView.name = this.formGroup.value.name;
secretView.value = this.formGroup.value.value; secretView.value = this.formGroup.value.value;
secretView.note = this.formGroup.value.notes; secretView.note = this.formGroup.value.notes;
secretView.projects = this.selectedProjects ? this.selectedProjects : emptyProjects; secretView.projects = [this.projects.find((p) => p.id == this.formGroup.value.project)];
return secretView; return secretView;
} }
} }