Feature 11034: Forgot Password in OSM
[osm/NG-UI.git] / src / services / SharedService.ts
1 /*
2  Copyright 2020 TATA ELXSI
3
4  Licensed under the Apache License, Version 2.0 (the 'License');
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15
16  Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
17  */
18 /**
19  * @file Provider for Shared Service
20  */
21 import { HttpErrorResponse, HttpHeaders } from '@angular/common/http';
22 import { EventEmitter, Injectable, Output } from '@angular/core';
23 import { FormArray, FormGroup } from '@angular/forms';
24 import { Router } from '@angular/router';
25 import { TranslateService } from '@ngx-translate/core';
26 import {
27     CONSTANTNUMBER,
28     DOMAINS,
29     ERRORDATA,
30     FILESETTINGS,
31     GETAPIURLHEADER,
32     PACKAGEINFO,
33     PAGERSMARTTABLE,
34     SMARTTABLECLASS,
35     TARSETTINGS,
36     TYPESECTION
37 } from 'CommonModel';
38 import { environment } from 'environment';
39 import * as HttpStatus from 'http-status-codes';
40 import * as untar from 'js-untar';
41 import { ActiveToast, ToastrService } from 'ngx-toastr';
42 import * as pako from 'pako';
43 import { RestService } from 'RestService';
44 import { Observable } from 'rxjs';
45 import { map } from 'rxjs/operators';
46
47 /** This is added globally by the tar.js library */
48 // eslint-disable-next-line @typescript-eslint/no-explicit-any
49 declare const Tar: any;
50
51 /**
52  * An Injectable is a class adorned with the @Injectable decorator function.
53  * @Injectable takes a metadata object that tells Angular how to compile and run module code
54  */
55 @Injectable({
56     providedIn: 'root'
57 })
58 /** Exporting a class @exports SharedService */
59 export class SharedService {
60     /** call the parent using event information @private */
61     @Output() public dataEvent: EventEmitter<{}> = new EventEmitter<{}>();
62
63     /** Variables to hold regexp pattern for URL */
64     public REGX_URL_PATTERN: RegExp = new RegExp(/^(http?|ftp|https):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z0-9]{2,15})(:((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4})))*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/);
65
66     /** Variables to hold regexp pattern for IP Address */
67     public REGX_IP_PATTERN: RegExp = new RegExp(/^(?:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(?!$)|$)){4}$/);
68
69     /** Variables to hold regexp pattern for Port Number */
70     public REGX_PORT_PATTERN: RegExp = new RegExp(/^((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}))$/);
71
72     /** Variables to hold regexp pattern for DPID */
73     public REGX_DPID_PATTERN: RegExp = new RegExp(/^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){7}$/);
74
75     /** Variable to hold regexp pattern for password */
76     public REGX_PASSWORD_PATTERN: RegExp = new RegExp(/^.*(?=.{8,})((?=.*[!@#$%^&*()\-_=+{};:,<.>]){1})(?=.*\d)((?=.*[a-z]){1})((?=.*[A-Z]){1}).*$/);
77
78     /** Variables to hold regexp pattern for Latitude */
79     public REGX_LAT_PATTERN: RegExp = new RegExp(/^(\+|-)?(?:90(?:(?:\.0{1,6})?)|(?:[0-9]|[1-8][0-9])(?:(?:\.[0-9]{1,15})?))$/);
80
81     /** Variables to hold regexp pattern for Longitude */
82     public REGX_LONG_PATTERN: RegExp = new RegExp(/^(\+|-)?(?:180(?:(?:\.0{1,6})?)|(?:[0-9]|[1-9][0-9]|1[0-7][0-9])(?:(?:\.[0-9]{1,15})?))$/);
83
84     /** Variable to hold regexp pattern for EMAIL */
85     public REGX_EMAIL_PATTERN: RegExp = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
86
87     /** Variables to hold maxlength for the description @public */
88     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
89     public MAX_LENGTH_DESCRIPTION: number = 500;
90
91     /** Variables to hold maxlength for the name @public */
92     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
93     public MAX_LENGTH_NAME: number = 50;
94
95     /** FormGroup instance added to the form @ html @public */
96     public formGroup: FormGroup;
97
98     /** Controls the go to top button on scroll  @public */
99     public showGotoTop: boolean;
100
101     /** Holds OSM Version value @public */
102     public osmVersion: string;
103
104     /** Holds Last Login Toaster Message @public */
105     public lastLoginMessage: string;
106
107     /** Holds Failed Attempts Toaster Message @public */
108     public failedAttemptsMessage: string;
109
110     /** Holds No Of Days Toaster Message @public */
111     public daysMessage: string;
112
113     /** express number for time manupulation -2 */
114     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
115     private epochTimeMinus2: number = -2;
116
117     /** express number for time manupulation 1000 */
118     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
119     private epochTime1000: number = 1000;
120
121     /** express number for time manupulation 60 */
122     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
123     private epochTime60: number = 60;
124
125     /** express number for time manupulation 24 */
126     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
127     private epochTime24: number = 24;
128
129     /** Random string generator length */
130     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
131     private randomStringLength: number = 4;
132
133     /** express number for rgb manipulation */
134     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
135     private colourHour: number = 10;
136
137     /** express number for rgb manipulation*/
138     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
139     private colourMin: number = 5;
140
141     /** Instance of the rest service @private */
142     private restService: RestService;
143
144     /** Service holds the router information @private */
145     private router: Router;
146
147     /** Check for the root directory @private */
148     // eslint-disable-next-line @typescript-eslint/no-magic-numbers
149     private directoryCount: number = 2;
150
151     /** express number for time manupulation 1000 */
152     private toasterSettings: {} = {
153         enableHtml: true,
154         closeButton: true,
155         timeOut: 2000
156     };
157
158     /** Contains tranlsate instance @private */
159     private translateService: TranslateService;
160
161     /** Contains toaster instance @private */
162     private toaster: ToastrService;
163
164     constructor(restService: RestService, router: Router, translateService: TranslateService, toaster: ToastrService) {
165         this.restService = restService;
166         this.router = router;
167         this.translateService = translateService;
168         this.toaster = toaster;
169     }
170
171     /** convert epoch time function @public */
172     public convertEpochTime(unixtimestamp: number): string {
173         if (!isNullOrUndefined(unixtimestamp)) {
174             const monthsArr: string[] = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
175                 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
176             const date: Date = new Date(unixtimestamp * this.epochTime1000);
177             const year: number = date.getFullYear();
178             const month: string = monthsArr[date.getMonth()];
179             const day: number = date.getDate();
180             const hours: number = date.getHours();
181             const minutes: string = '0' + date.getMinutes();
182             const seconds: string = '0' + date.getSeconds();
183             // eslint-disable-next-line deprecation/deprecation
184             return month + '-' + day + '-' + year + ' ' + hours + ':' + minutes.substr(this.epochTimeMinus2) + ':'
185                 // eslint-disable-next-line deprecation/deprecation
186                 + seconds.substr(this.epochTimeMinus2);
187         }
188         return this.translateService.instant('NODATE');
189     }
190
191     /** convert epoch time function to No of days @public */
192     public converEpochToDays(date: string): number {
193         if (!isNullOrUndefined(date)) {
194             const today: Date = new Date();
195             const accountDate: Date = new Date(date);
196             const toasterDate: number = (accountDate.getTime() -
197                 today.getTime()) / this.epochTime1000 / this.epochTime60 / this.epochTime60 / this.epochTime24;
198             if (toasterDate >= 0 || toasterDate < 1) {
199                 return Math.round(toasterDate);
200             }
201             return Math.floor(toasterDate);
202         }
203         return this.translateService.instant('N/A');
204     }
205
206     /** show toaster for password & account expiry @public */
207     public showToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
208         accountNoOfDays: string, passwordExpireMessage: string, accountExpireMessage: string,
209         passwordMessage: string, accountMessage: string): ActiveToast<string> {
210         this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
211         this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
212         return this.toaster.info(this.lastLoginMessage + ':' + '&nbsp' + lastLogin +
213             '</br>' + this.failedAttemptsMessage + ':' + '&nbsp' + failedAttempts +
214             '</br>' + passwordExpireMessage + '&nbsp' + passwordNoOfDays + '&nbsp' + passwordMessage +
215             '</br>' + accountExpireMessage + '&nbsp' + accountNoOfDays + '&nbsp' + accountMessage,
216             this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
217     }
218
219     /** show toaster for password expiry @public */
220     public passwordToaster(lastLogin: string, failedAttempts: string, passwordNoOfDays: string,
221         passwordExpireMessage: string, passwordMessage: string): ActiveToast<string> {
222         this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
223         this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
224         return this.toaster.info(this.lastLoginMessage + ':' + '&nbsp' + lastLogin +
225             '</br>' + this.failedAttemptsMessage + ':' + '&nbsp' + failedAttempts +
226             '</br>' + passwordExpireMessage + '&nbsp' + passwordNoOfDays + '&nbsp' + passwordMessage,
227             this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
228     }
229
230     /** show toaster for account expiry @public */
231     public accountToaster(lastLogin: string, failedAttempts: string,
232         accountNoOfDays: string, accountExpireMessage: string, accountMessage: string): ActiveToast<string> {
233         this.lastLoginMessage = this.translateService.instant('PAGE.LOGIN.LASTACCESS');
234         this.failedAttemptsMessage = this.translateService.instant('PAGE.LOGIN.FAILED');
235         return this.toaster.info(this.lastLoginMessage + ':' + '&nbsp' + lastLogin +
236             '</br>' + this.failedAttemptsMessage + ':' + '&nbsp' + failedAttempts +
237             '</br>' + accountExpireMessage + '&nbsp' + accountNoOfDays + '&nbsp' + accountMessage,
238             this.translateService.instant('PAGE.LOGIN.LOGINHISTORY'), this.toasterSettings);
239     }
240
241     /** Download Files function @public */
242     public downloadFiles(name: string, binaryData: Blob[], filetype: string): void {
243         const downloadLink: HTMLAnchorElement = document.createElement('a');
244         downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: filetype }));
245         if (name !== undefined) {
246             // eslint-disable-next-line @typescript-eslint/no-explicit-any
247             const newVariable: any = window.navigator;
248             if (newVariable.msSaveOrOpenBlob) {
249                 newVariable.msSaveBlob(new Blob(binaryData, { type: filetype }), 'OSM_Export_' + name + '.tar.gz');
250             } else {
251                 downloadLink.setAttribute('download', 'OSM_Export_' + name + '.tar.gz');
252                 document.body.appendChild(downloadLink);
253                 downloadLink.click();
254             }
255         }
256     }
257
258     /** Call this method after delete perform action is completed in the ng-smart-table data @public */
259     public callData(): void {
260         this.dataEvent.emit();
261     }
262
263     /** Generate random string @public */
264     public randomString(): string {
265         let result: string = '';
266         for (let randomStringRef: number = this.randomStringLength; randomStringRef > 0; --randomStringRef) {
267             result += new Date().getSeconds();
268         }
269         return result;
270     }
271
272     /** Function to read uploaded file String @public */
273     public async getFileString(files: FileList, fileType: string): Promise<string | ArrayBuffer> {
274         const reader: FileReader = new FileReader();
275         return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
276             if (this.vaildataFileInfo(files[0], fileType)) {
277                 this.readFileContent(reader, files[0], fileType);
278             } else {
279                 reject('typeError');
280             }
281             reader.onload = (): void => {
282                 if (reader.result === null) {
283                     reject('contentError');
284                 }
285                 resolve(reader.result);
286             };
287             reader.onerror = (event: Event): void => {
288                 reject('contentError');
289             };
290         });
291     }
292
293     /** Method to handle tar and tar.gz file for shared YAML file content @public */
294     public async targzFile(packageInfo: PACKAGEINFO): Promise<string | ArrayBuffer> {
295         return new Promise<string | ArrayBuffer>((resolve: Function, reject: Function): void => {
296             const httpOptions: GETAPIURLHEADER = this.getHttpOptions();
297             let apiUrl: string = '';
298             apiUrl = packageInfo.packageType === 'nsd' ? environment.NSDESCRIPTORS_URL + '/' + packageInfo.id + '/nsd_content' :
299                 environment.VNFPACKAGES_URL + '/' + packageInfo.id + '/package_content';
300             this.restService.getResource(apiUrl, httpOptions).subscribe((response: ArrayBuffer): void => {
301                 try {
302                     // eslint-disable-next-line @typescript-eslint/no-explicit-any
303                     const tar: any = new Tar();
304                     const originalInput: Uint8Array = pako.inflate(response, { to: 'Uint8Array' });
305                     untar(originalInput.buffer).then((extractedFiles: TARSETTINGS[]): void => {
306                         const getFoldersFiles: {}[] = extractedFiles;
307                         const folderNameStr: string = extractedFiles[0].name;
308                         getFoldersFiles.forEach((value: TARSETTINGS): void => {
309                             const fileValueObj: FILESETTINGS = this.createFileValueObject(value);
310                             const getRootFolder: string[] = value.name.split('/');
311                             if (value.name.startsWith(folderNameStr) &&
312                                 (value.name.endsWith('.yaml') || value.name.endsWith('.yml')) &&
313                                 getRootFolder.length === this.directoryCount) {
314                                 tar.append(value.name, packageInfo.descriptor, fileValueObj);
315                             } else {
316                                 if (value.type !== 'L') {
317                                     tar.append(value.name, new Uint8Array(value.buffer), fileValueObj);
318                                 }
319                             }
320                         });
321                         const out: Uint8Array = tar.out;
322                         const originalOutput: Uint8Array = pako.gzip(out);
323                         resolve(originalOutput.buffer);
324                     }, (err: string): void => {
325                         reject('');
326                     });
327                 } catch (e) {
328                     reject('');
329                 }
330             }, (error: HttpErrorResponse): void => {
331                 if (error.status === HttpStatus.NOT_FOUND || error.status === HttpStatus.UNAUTHORIZED) {
332                     this.router.navigateByUrl('404', { skipLocationChange: true }).catch((): void => {
333                         // Catch Navigation Error
334                     });
335                 } else {
336                     this.restService.handleError(error, 'get');
337                     reject('');
338                 }
339             });
340         });
341     }
342
343     /** Method to return the file information @public */
344     public createFileValueObject(value: TARSETTINGS): FILESETTINGS {
345         return {
346             type: value.type,
347             linkname: value.linkname,
348             owner: value.uname,
349             group: value.gname
350         };
351     }
352
353     /** Method to check given string is JSON or not @public */
354     public checkJson(jsonString: string): boolean {
355         jsonString = jsonString.replace(/'/g, '"');
356         try {
357             JSON.parse(jsonString);
358         } catch (e) {
359             return false;
360         }
361         return true;
362     }
363
364     /** Clean the form before submit @public */
365     public cleanForm(formGroup: FormGroup, formName?: String): void {
366         Object.keys(formGroup.controls).forEach((key: string) => {
367             if ((!isNullOrUndefined((formGroup.get(key) as FormArray | FormGroup).controls)) && key !== 'config') {
368                 // eslint-disable-next-line @typescript-eslint/no-shadow
369                 for (const { item, index } of (formGroup.get(key).value).map((item: {}, index: number) => ({ item, index }))) {
370                     // eslint-disable-next-line security/detect-object-injection
371                     const newFormGroup: FormGroup = (formGroup.get(key) as FormArray).controls[index] as FormGroup;
372                     this.cleanForm(newFormGroup);
373                 }
374             } else if (formGroup.get(key).value !== undefined && formGroup.get(key).value !== null && key !== 'config') {
375                 if (!Array.isArray(formGroup.get(key).value)) {
376                     if (typeof formGroup.get(key).value === 'string') {
377                         formGroup.get(key).setValue(formGroup.get(key).value.trim());
378                     }
379                 }
380             } else if (key === 'config' && formName === 'vim') {
381                 const newFormGroup: FormGroup = formGroup.get(key) as FormGroup;
382                 this.cleanForm(newFormGroup);
383             }
384         });
385     }
386
387     /** Method to return the config of pager value for ngSmarttable @public */
388     public paginationPagerConfig(): PAGERSMARTTABLE {
389         return {
390             display: true,
391             perPage: environment.paginationNumber
392         };
393     }
394
395     /** Method to return the class for the table for ngSmarttable @public */
396     public tableClassConfig(): SMARTTABLECLASS {
397         return {
398             class: 'table list-data'
399         };
400     }
401
402     /** Method to return all languages name and its code @public */
403     public languageCodeList(): {}[] {
404         return [
405             { code: 'en', language: 'English' },
406             { code: 'es', language: 'Spanish' },
407             { code: 'pt', language: 'Portuguese' },
408             { code: 'de', language: 'German' }
409         ];
410     }
411
412     /** Fetch OSM Version @public */
413     public fetchOSMVersion(): void {
414         this.restService.getResource(environment.OSM_VERSION_URL).subscribe((res: { version: string }): void => {
415             const version: string[] = res.version.split('+');
416             if (!isNullOrUndefined(version[0])) {
417                 this.osmVersion = version[0];
418                 sessionStorage.setItem('version', version[0]);
419             } else {
420                 this.osmVersion = null;
421             }
422         }, (error: ERRORDATA): void => {
423             this.osmVersion = null;
424             this.restService.handleError(error, 'get');
425         });
426     }
427
428     /** Random RGB color code generator @public */
429     public generateColor(): string {
430         const x: number = Math.floor((new Date().getHours()) * this.colourHour);
431         const y: number = Math.floor((new Date().getMinutes()) * this.colourMin);
432         const z: number = Math.floor((new Date().getSeconds()) * this.colourMin);
433         return 'rgb(' + x + ',' + y + ',' + z + ')';
434     }
435
436     /** Add custom name/tag to the dropdown @public */
437     public addCustomTag(tag: string): string {
438         return tag;
439     }
440
441     /** Fetch file extension @public */
442     public fetchFileExtension(fileInfo: FileList): string {
443         return fileInfo[0].name.substring(fileInfo[0].name.lastIndexOf('.') + 1);
444     }
445
446     /** Get domain name @private */
447     public getDomainName(): Observable<TYPESECTION[]> {
448         return this.restService.getResource(environment.DOMAIN_URL).pipe(map((domains: DOMAINS): TYPESECTION[] => {
449             const domainList: TYPESECTION[] = [];
450             try {
451                 let domainNames: string[] = [];
452                 if (!isNullOrUndefined(domains.project_domain_name)) {
453                     domainNames = domainNames.concat(domains.project_domain_name.split(','));
454                 }
455                 if (!isNullOrUndefined(domains.user_domain_name)) {
456                     domainNames = domainNames.concat(domains.user_domain_name.split(','));
457                 }
458                 domainNames = Array.from(new Set(domainNames));
459                 if (domainNames.length > 0) {
460                     domainNames.forEach((domainName: string): void => {
461                         if (!domainName.endsWith(':ro')) {
462                             domainList.push({ title: domainName, value: domainName });
463                         }
464                     });
465                 }
466                 return domainList;
467             } catch (e) {
468                 return domainList;
469             }
470         }));
471     }
472
473     /** Sorting the list based on date @public */
474     public compareFunction = (dir: number, a: string, b: string): number => {
475         const first: number = new Date(a).getTime();
476         const second: number = new Date(b).getTime();
477         if (first < second) {
478             return -1 * dir;
479         }
480         if (first > second) {
481             return dir;
482         }
483         return 0;
484     };
485
486     /** Method to validate file extension and size @private */
487     private vaildataFileInfo(fileInfo: File, fileType: string): boolean {
488         const extension: string = fileInfo.name.substring(fileInfo.name.lastIndexOf('.') + 1);
489         const packageSize: number = CONSTANTNUMBER.oneMB * environment.packageSize;
490         if (fileType === 'yaml' && (extension.toLowerCase() === 'yaml' || extension.toLowerCase() === 'yml')
491             && fileInfo.size <= packageSize) {
492             return true;
493         } else if (extension.toLowerCase() === fileType && fileInfo.size <= packageSize) {
494             return true;
495         }
496         return false;
497     }
498
499     /** Method to read file content based on type @private */
500     private readFileContent(reader: FileReader, fileInfo: File, fileType: string): void {
501         if (fileType === 'gz') {
502             reader.readAsArrayBuffer(fileInfo);
503         } else {
504             reader.readAsText(fileInfo);
505         }
506     }
507
508     /** Method to handle http options @public */
509     private getHttpOptions(): GETAPIURLHEADER {
510         return {
511             headers: new HttpHeaders({
512                 Accept: 'application/gzip, application/json',
513                 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
514             }),
515             responseType: 'arraybuffer'
516         };
517     }
518 }
519
520 /** Method to handle null or undefined @public */
521 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
522 export const isNullOrUndefined = (data: any): boolean => data === null || data === undefined;
523