Skip to content

Commit

Permalink
Merge pull request #4166 from cisagov/bug/CSET-2874
Browse files Browse the repository at this point in the history
Users try and remove each others facilitator roles at same time: Bug/cset 2874
  • Loading branch information
randywoods authored Oct 16, 2024
2 parents 83d5352 + c9f0d6d commit 9027262
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,14 @@ public IActionResult PostUpdateUser([FromBody] CreateUser userBeingUpdated)
if (assessmentId >= 0)
{
// Updating a Contact in the context of the current Assessment.
(_token).AuthorizeAdminRole();
try
{
(_token).AuthorizeAdminRole();
}
catch
{
return Forbid();
}

int newUserId = (int)userBeingUpdated.UserId;

Expand Down
4 changes: 3 additions & 1 deletion CSETWebNg/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ import { AllAnsweredquestionsComponent } from './reports/all-answeredquestions/a
import { AllCommentsmarkedComponent } from './reports/all-commentsmarked/all-commentsmarked.component';
import { AllReviewedComponent } from './reports/all-reviewed/all-reviewed.component';
import { QuestionsReviewedComponent } from './reports/questions-reviewed/questions-reviewed.component';
import { RolesChangedComponent } from './dialogs/roles-changed/roles-changed.component';


@NgModule({ declarations: [
Expand Down Expand Up @@ -1202,7 +1203,8 @@ import { QuestionsReviewedComponent } from './reports/questions-reviewed/questio
AllAnsweredquestionsComponent,
AllCommentsmarkedComponent,
AllReviewedComponent,
QuestionsReviewedComponent
QuestionsReviewedComponent,
RolesChangedComponent
],
bootstrap: [AppComponent], imports: [BrowserModule,
BrowserAnimationsModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h4>{{ t('contacts') }}</h4>
<div class="list-group">
<app-contact-item [contact]="contact" [contactsList]="contacts" *ngFor="let contact of contacts; let i = index"
(startEditEvent)="startEdit()" (abandonEditEvent)="abandonEdit()" (edit)="editContact(contact)"
(create)="saveNewContact(contact)" (remove)="removeContact(contact, i)">
(create)="saveNewContact(contact)" (remove)="removeContact(contact, i)" (rolesChangedEvent)="refreshContacts()">
</app-contact-item>
<span class="mt-2 btn-group" *ngIf="showControls()">
<button class="btn btn-group d-flex align-items-center flex-00a" (click)="newContact()" [disabled]="adding==1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,31 @@ export class AssessmentContactsComponent implements OnInit {
* Fires when a contact's edit is complete.
*/
editContact(contact: User) {
this.assessSvc.updateContact(contact).subscribe(data => {
if (data && data.userId != contact.userId) {
// Update the userId in case changing email linked to new user in backend
this.contacts.find(x => x.userId === contact.userId).userId = data.userId;
this.assessSvc
.getAssessmentContacts()
.then((data: AssessmentContactsResponse) => {
if (data.currentUserRole == 2) {
try {
this.assessSvc.updateContact(contact).subscribe(data => {
if (data && data.userId != contact.userId) {
// Update the userId in case changing email linked to new user in backend
this.contacts.find(x => x.userId === contact.userId).userId = data.userId;
}
this.contactItems.forEach(x => x.enableMyControls = true);
this.changeOccurred();
});
} catch (error) {
console.error(error)
}
} else {
console.error("User does not have the correct role to edit")
}
this.contactItems.forEach(x => x.enableMyControls = true);
this.changeOccurred();
});
})
}

refreshContacts(){
this.contacts = [] as EditableUser[];
this.ngOnInit();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { EmailService } from "../../../../../services/email.service";
import { LayoutService } from "../../../../../services/layout.service";
import { TranslocoService } from "@jsverse/transloco";
import { DemographicIodService } from "../../../../../services/demographic-iod.service";
import { AssessmentContactsResponse } from "../../../../../models/assessment-info.model";
import { RolesChangedComponent } from "../../../../../dialogs/roles-changed/roles-changed.component";

@Component({
selector: "app-contact-item",
Expand All @@ -60,6 +62,8 @@ export class ContactItemComponent implements OnInit {
abandonEditEvent = new EventEmitter<boolean>();
@Output()
edit = new EventEmitter<EditableUser>();
@Output()
rolesChangedEvent = new EventEmitter<boolean>();

@ViewChild('topScrollAnchor') topScroll;

Expand All @@ -70,7 +74,7 @@ export class ContactItemComponent implements OnInit {
results: EditableUser[];
roles: Role[];
editMode: boolean;
creatorId: any;
creatorId: any;


constructor(
Expand All @@ -80,7 +84,7 @@ export class ContactItemComponent implements OnInit {
private assessSvc: AssessmentService,
private dialog: MatDialog,
public layoutSvc: LayoutService,
public tSvc: TranslocoService,
public tSvc: TranslocoService,
public demoSvc: DemographicIodService
) {
this.editMode = true;
Expand Down Expand Up @@ -184,19 +188,33 @@ export class ContactItemComponent implements OnInit {
}

saveContact() {
if (this.contact.isNew) {
if (this.existsDuplicateEmail(this.contact.primaryEmail)) {
return;
}
this.assessSvc
.getAssessmentContacts()
.then((data: AssessmentContactsResponse) => {
if (data.currentUserRole == 2) {
if (this.contact.isNew) {
if (this.existsDuplicateEmail(this.contact.primaryEmail)) {
return;
}

this.create.emit(this.contact);
this.create.emit(this.contact);

this.contact.isNew = false;
this.editMode = true;
} else {
this.finishEdit();
}
return true;
this.contact.isNew = false;
this.editMode = true;
} else {
this.finishEdit();
}
return true;
} else {
this.scrollToTop();
this.abandonEdit();
let rolesChangeDialog = this.dialog.open(RolesChangedComponent)
rolesChangeDialog.afterClosed().subscribe(() => {
})
this.ngOnInit();
this.rolesChangedEvent.emit();
}
})
}

removeContact() {
Expand Down Expand Up @@ -275,7 +293,7 @@ export class ContactItemComponent implements OnInit {
this.topScroll?.nativeElement.scrollIntoView({ behavior: 'smooth', alignToTop: true });
}
// Check if assessment was created by current user
assessmentCreator(){
assessmentCreator() {
this.assessSvc.getCreator().then((response: any) => {
this.creatorId = response
})
Expand Down
8 changes: 3 additions & 5 deletions CSETWebNg/src/app/dialogs/ejection/ejection.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@
<div class="d-flex flex-column justify-content-center flex-11a" *transloco="let t">
<div class="mat-dialog-header p-3 d-flex justify-content-start align-items-center flex-00a">
<span class="cset-icons-exclamation-triangle fs-base-6 me-3"></span>
<span>Access Denied</span>
<span>{{t('dialogs.access denied')}}</span>
</div>
<mat-dialog-content class="p-3 pe-0 oy-auto d-flex flex-column flex-11a">
<p class="pe-3">Your session has expired, a connection error has occurred, or you are no longer authorized to access
that
assessment.</p>
<p>Please log in again.</p>
<p class="pe-3">{{t('dialogs.access denied 1')}}</p>
<p>{{t('dialogs.access denied 2')}}</p>
</mat-dialog-content>
<mat-dialog-actions class="p-3 pt-0 mb-0">
<button class="btn btn-secondary" (click)="close()">{{t('buttons.ok')}}</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!----------------------
Copyright 2024 Battelle Energy Alliance, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-------------------------->
<div class="d-flex flex-column justify-content-center flex-11a" *transloco="let t">
<div class="mat-dialog-header p-3 d-flex justify-content-start align-items-center flex-00a">
<span class="cset-icons-exclamation-triangle fs-base-6 me-3"></span>
<span>{{t('dialogs.access denied')}}</span>
</div>
<mat-dialog-content class="p-3 pe-0 oy-auto d-flex flex-column flex-11a">
<p class="pe-3">{{t('dialogs.access denied 3')}}</p>
</mat-dialog-content>
<mat-dialog-actions class="p-3 pt-0 mb-0">
<button class="btn btn-secondary" (click)="close()">{{t('buttons.ok')}}</button>
</mat-dialog-actions>
</div>
43 changes: 43 additions & 0 deletions CSETWebNg/src/app/dialogs/roles-changed/roles-changed.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
////////////////////////////////
//
// Copyright 2024 Battelle Energy Alliance, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
////////////////////////////////
import { Component, OnInit } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';

@Component({
selector: 'app-roles-changed',
templateUrl: './roles-changed.component.html',
// eslint-disable-next-line
host: { class: 'd-flex flex-column flex-11a' }
})
export class RolesChangedComponent implements OnInit {
constructor(private dialog: MatDialogRef<RolesChangedComponent>) { }

ngOnInit() {
sessionStorage.setItem('hasUserAgreedToPrivacyWarning', 'true');
}

close() {
return this.dialog.close();
}
}
6 changes: 5 additions & 1 deletion CSETWebNg/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,11 @@
"export assessment title": "Export Assessment Options",
"scrub pcii": "Select to remove PCII fields from export",
"encrypt 1": "To encrypt your assessment, please enter a password.",
"encrypt 2": "If you proceed without entering a password, no encryption will be used."
"encrypt 2": "If you proceed without entering a password, no encryption will be used.",
"access denied": "Access Denied",
"access denied 1": "Your session has expired, a connection error has occurred, or you are no longer authorized to access that assessment.",
"access denied 2": "Please log in again.",
"access denied 3": "Your role on this assessment has been changed by another user."
},
"reports": {
"launch": {
Expand Down
6 changes: 5 additions & 1 deletion CSETWebNg/src/assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,11 @@
"export assessment title": "Opciones de evaluación de exportación",
"scrub pcii": "Seleccione para eliminar los campos PCII de la exportación",
"encrypt 1": "Para cifrar su evaluación, ingrese una contraseña.",
"encrypt 2": "Si continúa sin ingresar una contraseña, no se utilizará ningún cifrado."
"encrypt 2": "Si continúa sin ingresar una contraseña, no se utilizará ningún cifrado.",
"access denied": "Acceso denegado",
"access denied 1": "Su sesión expiró, se produjo un error de conexión o ya no está autorizado a acceder a esa evaluación.",
"access denied 2": "Por favor inicia sesión nuevamente.",
"access denied 3": "Otro usuario ha cambiado su función en esta evaluación."
},
"reports": {
"assessment complete message": "Gracias por completar su evaluación. Los informes en esta página capturan resúmenes de sus resultados que pueden ayudar en la planificación y crecimiento de la ciberseguridad de su organización en el futuro. La evaluación fue actualizada por última vez el {date}.",
Expand Down
6 changes: 5 additions & 1 deletion CSETWebNg/src/assets/i18n/uk.json
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,11 @@
"export assessment title": "Параметри експортної оцінки",
"scrub pcii": "Виберіть, щоб видалити поля PCII з експорту",
"encrypt 1": "Щоб зашифрувати свою оцінку, введіть пароль.",
"encrypt 2": "Якщо ви продовжите без введення пароля, шифрування не використовуватиметься."
"encrypt 2": "Якщо ви продовжите без введення пароля, шифрування не використовуватиметься.",
"access denied": "Доступ заборонено",
"access denied 1": "Ваш сеанс закінчився, сталася помилка підключення або ви більше не маєте доступу до цього оцінювання.",
"access denied 2": "Будь ласка, увійдіть знову.",
"access denied 3": "Вашу роль у цьому оцінюванні змінив інший користувач."
},
"reports": {
"launch": {
Expand Down

0 comments on commit 9027262

Please sign in to comment.