From 387c57b3ea75e2aefeb4abacf274ab35f3dc3079 Mon Sep 17 00:00:00 2001 From: Vladimir Vinogradenko Date: Mon, 25 Sep 2023 11:42:33 -0400 Subject: [PATCH 1/2] Empty commit to create PR on github. You should reset it From 04b77ff9c76f73c6ad7a297632a9f57e1be3999f Mon Sep 17 00:00:00 2001 From: Oleksandr Karpov Date: Fri, 29 Sep 2023 14:34:00 +0300 Subject: [PATCH 2/2] NAS-124260: PR update --- src/app/helptext/system/advanced.ts | 4 +- .../form-checkbox.component.html | 18 ++- .../form-checkbox/form-checkbox.component.ts | 4 +- .../models/field-config.interface.ts | 90 ++++++++--- .../fn-support/fn-support.component.ts | 152 +++++++++++------- .../services/attach-debug-warning.service.ts | 31 ++++ .../tn-support/tn-support.component.ts | 101 ++++++++---- src/app/pages/system/system.module.ts | 6 +- 8 files changed, 285 insertions(+), 121 deletions(-) create mode 100644 src/app/pages/system/support/services/attach-debug-warning.service.ts diff --git a/src/app/helptext/system/advanced.ts b/src/app/helptext/system/advanced.ts index 13e2c338c9f..a83ed748d21 100644 --- a/src/app/helptext/system/advanced.ts +++ b/src/app/helptext/system/advanced.ts @@ -13,7 +13,9 @@ export const helptext_system_advanced = { fieldset_replication: T('Replication'), dialog_generate_debug_title: T('Generate Debug File'), - dialog_generate_debug_message: T('This operation might take a long time. Proceed?'), + dialog_generate_debug_message: T('Warning: Debugs may contain log files with personal information such\ + as usernames or other identifying information about your system. Please review debugs and redact any\ + sensitive information before sharing with external entities.'), dialog_button_ok: T('Proceed'), debug_dialog: { diff --git a/src/app/pages/common/entity/entity-form/components/form-checkbox/form-checkbox.component.html b/src/app/pages/common/entity/entity-form/components/form-checkbox/form-checkbox.component.html index 250a632c199..a670407d355 100644 --- a/src/app/pages/common/entity/entity-form/components/form-checkbox/form-checkbox.component.html +++ b/src/app/pages/common/entity/entity-form/components/form-checkbox/form-checkbox.component.html @@ -1,10 +1,24 @@
- + {{ config.placeholder | translate }} - + {{ config.placeholder | translate }} +
diff --git a/src/app/pages/common/entity/entity-form/components/form-checkbox/form-checkbox.component.ts b/src/app/pages/common/entity/entity-form/components/form-checkbox/form-checkbox.component.ts index c75f2ab905b..9bbfc084d9e 100644 --- a/src/app/pages/common/entity/entity-form/components/form-checkbox/form-checkbox.component.ts +++ b/src/app/pages/common/entity/entity-form/components/form-checkbox/form-checkbox.component.ts @@ -1,10 +1,8 @@ -import { Component, ViewContainerRef } from '@angular/core'; +import { Component } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; - import { FieldConfig } from '../../models/field-config.interface'; import { Field } from '../../models/field.interface'; -import { TooltipComponent } from '../tooltip/tooltip.component'; @Component({ selector: 'form-checkbox', diff --git a/src/app/pages/common/entity/entity-form/models/field-config.interface.ts b/src/app/pages/common/entity/entity-form/models/field-config.interface.ts index d49744c49a7..3279d29ca03 100644 --- a/src/app/pages/common/entity/entity-form/models/field-config.interface.ts +++ b/src/app/pages/common/entity/entity-form/models/field-config.interface.ts @@ -13,23 +13,77 @@ export interface InputUnitConfig { } export interface FieldConfig { - disabled?: boolean; label?: string; inlineLabel?: string; name: string; options?: any[]; - errors?: string; hasErrors?: boolean; placeholder?: string; type: string; - inputType?: string; inputUnit?: InputUnitConfig; validation?: any[] | ValidatorFn | ValidatorFn[]; + disabled?: boolean; + label?: string; + inlineLabel?: string; + name: string; + options?: any[]; + errors?: string; + hasErrors?: boolean; + placeholder?: string; + type: string; + inputType?: string; + inputUnit?: InputUnitConfig; + validation?: any[] | ValidatorFn | ValidatorFn[]; asyncValidation?: AsyncValidatorFn | AsyncValidatorFn[]; - value?: any; multiple?: boolean; tristate?: boolean; tooltip?: string; tooltipPosition?: string; - relation?: RelationGroup[]; isHidden?: boolean; formarray?: any; - initialCount?: number; readonly?: boolean; initial?: string; rootSelectable?: boolean; - min?: number; max?: number; tabs?: any[]; tabName?: string; class?: string; - customEventActionLabel?: string; explorerType?: string; explorerParam?: any; customTemplateStringOptions?: any; - required?: boolean; deleteButtonOnFirst?: boolean; addBtnMessage?: string; - acceptedFiles?: string; fileLocation?: string; fileType?: string;width?: string; - message?: any; updater?: any; parent?: any;togglePw?: boolean; paraText?: any; - noexec?: boolean; blurStatus?: boolean;blurEvent?: any;noMinutes?: boolean; - warnings?: string; hideButton?: boolean; searchOptions?: any[]; hideDirs?: any; - listFields?: FieldConfig[][]; templateListField?: FieldConfig[]; - updateLocal?: boolean; isLoading?: boolean; textAreaRows?: number; netmaskPreset?: number; - isLargeText?: boolean; paragraphIcon?: string; paragraphIconSize?: string; zeroStateMessage?: string; isDoubleConfirm?: boolean; - maskValue?: any; hideErrMsg?: boolean; id?: string; autocomplete?: boolean; filereader?: boolean; - customEventMethod?(data: any); onChangeOption?(data: any); hint?: string; + value?: any; + multiple?: boolean; + tristate?: boolean; + tooltip?: string; + tooltipPosition?: string; + relation?: RelationGroup[]; + isHidden?: boolean; + formarray?: any; + initialCount?: number; + readonly?: boolean; + initial?: string; + rootSelectable?: boolean; + min?: number; + max?: number; + tabs?: any[]; + tabName?: string; + class?: string; + customEventActionLabel?: string; + explorerType?: string; + explorerParam?: any; + customTemplateStringOptions?: any; + required?: boolean; + deleteButtonOnFirst?: boolean; + addBtnMessage?: string; + acceptedFiles?: string; + fileLocation?: string; + fileType?: string; + width?: string; + message?: any; + updater?: any; + parent?: any; + togglePw?: boolean; + paraText?: any; + noexec?: boolean; + blurStatus?: boolean; + blurEvent?: any; + noMinutes?: boolean; + warnings?: string; + hideButton?: boolean; + searchOptions?: any[]; + hideDirs?: any; + listFields?: FieldConfig[][]; + templateListField?: FieldConfig[]; + updateLocal?: boolean; + isLoading?: boolean; + textAreaRows?: number; + netmaskPreset?: number; + isLargeText?: boolean; + paragraphIcon?: string; + paragraphIconSize?: string; + zeroStateMessage?: string; + isDoubleConfirm?: boolean; + maskValue?: any; + hideErrMsg?: boolean; + id?: string; + autocomplete?: boolean; + filereader?: boolean; + customEventMethod?(data: any); + onChangeOption?(data: any); + hint?: string; } diff --git a/src/app/pages/system/support/fn-support/fn-support.component.ts b/src/app/pages/system/support/fn-support/fn-support.component.ts index 199badf5f29..795d25baf7f 100644 --- a/src/app/pages/system/support/fn-support/fn-support.component.ts +++ b/src/app/pages/system/support/fn-support/fn-support.component.ts @@ -11,12 +11,15 @@ import { FieldSet } from 'app/pages/common/entity/entity-form/models/fieldset.in import { WebSocketService } from 'app/services/'; import { helptext_system_support as helptext } from 'app/helptext/system/support'; import { Subscription } from 'rxjs'; +import { AttachDebugWarningService } from 'app/pages/system/support/services/attach-debug-warning.service'; +import { FormControl } from '@angular/forms'; @Component({ selector: 'app-fn-support', template: '', }) export class FnSupportComponent implements OnDestroy { + private subscriptions = new Subscription(); entityEdit: EntityFormComponent; category: any; screenshot: any; @@ -118,27 +121,31 @@ export class FnSupportComponent implements OnDestroy { }, ]; - private categoriesSubscription: Subscription; - - constructor(protected ws: WebSocketService, protected dialog: MatDialog, - protected translate: TranslateService) { } + constructor( + protected ws: WebSocketService, + protected dialog: MatDialog, + protected translate: TranslateService, + private attachDebugWarningService: AttachDebugWarningService + ) { } afterInit(entityEdit: any) { this.entityEdit = entityEdit; setTimeout(() => { - this.translate.get(helptext.contactUs).subscribe((res) => { - _.find(this.fieldConfig, { name: 'FN_col2' }).paraText = 'mail' + res; - }); + this.subscriptions.add( + this.translate.get(helptext.contactUs).subscribe((res) => { + _.find(this.fieldConfig, { name: 'FN_col2' }).paraText = 'mail' + res; + }) + ); }, 2000); this.category = _.find(this.fieldConfig, { name: 'category' }); this.loadCategoriesOnAuth(); + + this.listenForAttachDebugChanges(); } ngOnDestroy(): void { - if (this.categoriesSubscription) { - this.categoriesSubscription.unsubscribe(); - } + this.subscriptions.unsubscribe(); } customSubmit(values): void { @@ -160,39 +167,49 @@ export class FnSupportComponent implements OnDestroy { let url; dialogRef.componentInstance.setCall('support.new_ticket', [payload]); dialogRef.componentInstance.submit(); - dialogRef.componentInstance.success.subscribe((res) => { - if (res.result) { - url = `${res.result.url}`; - } - if (res.method === 'support.new_ticket' && this.subs && this.subs.length > 0) { - this.subs.forEach((item) => { - const formData: FormData = new FormData(); - formData.append('data', JSON.stringify({ - method: 'support.attach_ticket', - params: [{ - ticket: (res.result.ticket), - filename: item.file.name, - token: payload.token, - }], - })); - formData.append('file', item.file, item.apiEndPoint); - dialogRef.componentInstance.wspost(item.apiEndPoint, formData); - dialogRef.componentInstance.success.subscribe(() => { - this.resetForm(); - }); - dialogRef.componentInstance.failure.subscribe((res) => { - dialogRef.componentInstance.setDescription(res.error); + this.subscriptions.add( + dialogRef.componentInstance.success.subscribe((res) => { + if (res.result) { + url = `${res.result.url}`; + } + if (res.method === 'support.new_ticket' && this.subs && this.subs.length > 0) { + this.subs.forEach((item) => { + const formData: FormData = new FormData(); + formData.append('data', JSON.stringify({ + method: 'support.attach_ticket', + params: [{ + ticket: (res.result.ticket), + filename: item.file.name, + token: payload.token, + }], + })); + formData.append('file', item.file, item.apiEndPoint); + dialogRef.componentInstance.wspost(item.apiEndPoint, formData); + this.subscriptions.add( + dialogRef.componentInstance.success.subscribe(() => { + this.resetForm(); + }) + ); + + this.subscriptions.add( + dialogRef.componentInstance.failure.subscribe((res) => { + dialogRef.componentInstance.setDescription(res.error); + }) + ); }); - }); - dialogRef.componentInstance.setDescription(url); - } else { - dialogRef.componentInstance.setDescription(url); - this.resetForm(); - } - }); - dialogRef.componentInstance.failure.subscribe((res) => { - dialogRef.componentInstance.setDescription(res.error); - }); + dialogRef.componentInstance.setDescription(url); + } else { + dialogRef.componentInstance.setDescription(url); + this.resetForm(); + } + }) + ); + + this.subscriptions.add( + dialogRef.componentInstance.failure.subscribe((res) => { + dialogRef.componentInstance.setDescription(res.error); + }) + ); } resetForm() { @@ -219,27 +236,38 @@ export class FnSupportComponent implements OnDestroy { } private loadCategoriesOnAuth(): void { - this.entityEdit.formGroup.controls['token'].valueChanges.subscribe((token) => { - if (!token) { - return; - } + this.subscriptions.add( + this.entityEdit.formGroup.controls['token'].valueChanges.subscribe((token) => { + if (!token) { + return; + } - this.category.isLoading = true; - this.ws.call('support.fetch_categories', [token]).subscribe((res) => { - this.category.isLoading = false; - this.entityEdit.setDisabled('category', false); - const options = []; - for (const property in res) { - if (res.hasOwnProperty(property)) { - options.push({ label: property, value: res[property] }); + this.category.isLoading = true; + this.ws.call('support.fetch_categories', [token]).subscribe((res) => { + this.category.isLoading = false; + this.entityEdit.setDisabled('category', false); + const options = []; + for (const property in res) { + if (res.hasOwnProperty(property)) { + options.push({ label: property, value: res[property] }); + } + this.category.options = _.sortBy(options, ['label']); } - this.category.options = _.sortBy(options, ['label']); - } - }, (error) => { - this.entityEdit.setDisabled('category', true); - this.category.isLoading = false; - new EntityUtils().handleWSError(this, error, this.dialog); - }); - }); + }, (error) => { + this.entityEdit.setDisabled('category', true); + this.category.isLoading = false; + new EntityUtils().handleWSError(this, error, this.dialog); + }); + }) + ); + } + + private listenForAttachDebugChanges(): void { + const control = this.entityEdit.formGroup.controls['attach_debug'] as FormControl; + + this.subscriptions.add( + this.attachDebugWarningService.handleAttachDebugChanges(control) + .subscribe((checked) => control.patchValue(checked)) + ); } } diff --git a/src/app/pages/system/support/services/attach-debug-warning.service.ts b/src/app/pages/system/support/services/attach-debug-warning.service.ts new file mode 100644 index 00000000000..dd8b71a667b --- /dev/null +++ b/src/app/pages/system/support/services/attach-debug-warning.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; +import { filter, pairwise, startWith, switchMap } from 'rxjs/operators'; +import { DialogService } from 'app/services/dialog.service'; + +@Injectable() +export class AttachDebugWarningService { + + constructor(private dialogService: DialogService, private translate: TranslateService) { } + + handleAttachDebugChanges(control: FormControl): Observable { + return control.valueChanges.pipe( + startWith(null), + pairwise(), + filter(([previousValue, currentValue]) => !previousValue && currentValue), + switchMap(() => this.showConfirmationDialog()), + ); + } + + showConfirmationDialog(): Observable { + return this.dialogService + .confirm({ + title: this.translate.instant('Warning'), + message: 'Debugs may contain log files with personal information such as usernames or other identifying information about your system. Debugs by default are attached privately to Jira tickets and only visible by iXsystem’s Engineering Staff. Please review debugs and redact any sensitive information before sharing with external entities. Debugs can be manually generated from System → Advanced → Save Debug', + hideCheckBox: true, + buttonMsg: this.translate.instant('Agree'), + }); + } +} diff --git a/src/app/pages/system/support/tn-support/tn-support.component.ts b/src/app/pages/system/support/tn-support/tn-support.component.ts index 3b890b6c084..c842e168342 100644 --- a/src/app/pages/system/support/tn-support/tn-support.component.ts +++ b/src/app/pages/system/support/tn-support/tn-support.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { TranslateService } from '@ngx-translate/core'; import { Router } from '@angular/router'; @@ -11,13 +11,16 @@ import { EntityJobComponent } from 'app/pages/common/entity/entity-job/entity-jo import { FieldConfig } from 'app/pages/common/entity/entity-form/models/field-config.interface'; import { FieldSet } from 'app/pages/common/entity/entity-form/models/fieldset.interface'; import { helptext_system_support as helptext } from 'app/helptext/system/support'; +import { AttachDebugWarningService } from 'app/pages/system/support/services/attach-debug-warning.service'; +import { Subscription } from 'rxjs'; @Component({ selector: 'app-tn-support', template: '', }) -export class TnSupportComponent implements OnInit { +export class TnSupportComponent implements OnInit, OnDestroy { + private subscriptions = new Subscription(); entityEdit: any; screenshot: any; subs: any; @@ -162,9 +165,15 @@ export class TnSupportComponent implements OnInit { }, ]; - constructor(public dialog: MatDialog, public loader: AppLoaderService, - public ws: WebSocketService, public dialogService: DialogService, public router: Router, - private translate: TranslateService) { } + constructor( + public dialog: MatDialog, + public loader: AppLoaderService, + public ws: WebSocketService, + public dialogService: DialogService, + public router: Router, + private translate: TranslateService, + private attachDebugWarningService: AttachDebugWarningService + ) { } ngOnInit() { setTimeout(() => { @@ -172,6 +181,11 @@ export class TnSupportComponent implements OnInit { _.find(this.fieldConfig, { name: 'FN_col2' }).paraText = 'mail' + res; }); }, 2000); + + } + + ngOnDestroy(): void { + this.subscriptions.unsubscribe(); } afterInit(entityEdit: any) { @@ -193,6 +207,8 @@ export class TnSupportComponent implements OnInit { }, }, ]; + + this.listenForAttachDebugChanges(); } emailListValidator(name: string) { @@ -250,35 +266,45 @@ export class TnSupportComponent implements OnInit { let url; dialogRef.componentInstance.setCall('support.new_ticket', [payload]); dialogRef.componentInstance.submit(); - dialogRef.componentInstance.success.subscribe((res) => { - if (res.result) { - url = `${res.result.url}`; - } - if (res.method === 'support.new_ticket' && this.subs && this.subs.length > 0) { - this.subs.forEach((item) => { - const formData: FormData = new FormData(); - formData.append('data', JSON.stringify({ - method: 'support.attach_ticket', - params: [{ ticket: (res.result.ticket), filename: item.file.name }], - })); - formData.append('file', item.file, item.apiEndPoint); - dialogRef.componentInstance.wspost(item.apiEndPoint, formData); - dialogRef.componentInstance.success.subscribe((res) => { - this.resetForm(); - }), - dialogRef.componentInstance.failure.subscribe((res) => { - dialogRef.componentInstance.setDescription(res.error); + this.subscriptions.add( + dialogRef.componentInstance.success.subscribe((res) => { + if (res.result) { + url = `${res.result.url}`; + } + if (res.method === 'support.new_ticket' && this.subs && this.subs.length > 0) { + this.subs.forEach((item) => { + const formData: FormData = new FormData(); + formData.append('data', JSON.stringify({ + method: 'support.attach_ticket', + params: [{ ticket: (res.result.ticket), filename: item.file.name }], + })); + formData.append('file', item.file, item.apiEndPoint); + dialogRef.componentInstance.wspost(item.apiEndPoint, formData); + this.subscriptions.add( + dialogRef.componentInstance.success.subscribe((res) => { + this.resetForm(); + }) + ); + + this.subscriptions.add( + dialogRef.componentInstance.failure.subscribe((res) => { + dialogRef.componentInstance.setDescription(res.error); + }) + ); }); - }); - dialogRef.componentInstance.setDescription(url); - } else { - dialogRef.componentInstance.setDescription(url); - this.resetForm(); - } - }); - dialogRef.componentInstance.failure.subscribe((res) => { - dialogRef.componentInstance.setDescription(res.error); - }); + dialogRef.componentInstance.setDescription(url); + } else { + dialogRef.componentInstance.setDescription(url); + this.resetForm(); + } + }) + ); + + this.subscriptions.add( + dialogRef.componentInstance.failure.subscribe((res) => { + dialogRef.componentInstance.setDescription(res.error); + }) + ); } updater(file: any, parent: any) { @@ -305,4 +331,13 @@ export class TnSupportComponent implements OnInit { this.entityEdit.formGroup.controls['criticality'].setValue('inquiry'); this.subs = []; } + + private listenForAttachDebugChanges(): void { + const control = this.entityEdit.formGroup.controls['attach_debug'] as FormControl; + + this.subscriptions.add( + this.attachDebugWarningService.handleAttachDebugChanges(control) + .subscribe((checked) => control.patchValue(checked)) + ); + } } diff --git a/src/app/pages/system/system.module.ts b/src/app/pages/system/system.module.ts index ed6fed00ee4..f0474fdce4a 100644 --- a/src/app/pages/system/system.module.ts +++ b/src/app/pages/system/system.module.ts @@ -5,7 +5,7 @@ import { NgxUploaderModule } from 'ngx-uploader'; import { QRCodeModule } from 'angular2-qrcode'; import { EntityModule } from '../common/entity/entity.module'; - +import { AttachDebugWarningService } from './support/services/attach-debug-warning.service' import { MaterialModule } from '../../appMaterial.module'; import { MarkdownModule } from 'ngx-markdown'; import { routing } from './system.routing'; @@ -125,6 +125,8 @@ import { QRDialog } from './two-factor/two-factor.component'; QRDialog, ], entryComponents: [QRDialog], - providers: [], + providers: [ + AttachDebugWarningService + ], }) export class SystemModule {}