import { Injectable } from '@angular/core'; import { Request } from '../models/request'; import { TranslatorService } from '../translator/translator.service'; import { ConnectorService } from '../connector/connector.service'; import { Observable, throwError } from 'rxjs'; import { CheckPatientRegisterationRequest } from './models/check-patient-registeration.request'; import { LoginRequest } from './models/login.request'; import { Response } from '../models/response'; import { AuthenticatedUser } from './models/authenticated-user'; import { CommonService } from '../common/common.service'; import { CheckUserAuthenticationRequest } from './models/check-user-auth.request'; import { CheckActivationCodeRequest } from './models/check-activation-code.request'; import { CheckUserAuthenticationResponse } from './models/check-user-auth.response'; import { CheckActivationCodeResponse } from './models/check-activation-code.response'; import { NativeStorage } from '@ionic-native/native-storage/ngx'; import { CheckRegisterationCodeRequest } from './models/check-registeration-code.request'; import { RegisterInformationRequest } from './models/register-information.request'; import { ForgotFileIDResponse } from './models/forgot-File-ID.response'; import { EmailRequest } from '../models/email-request'; import { EmailInput } from '../../ui/email/models/email-input'; import { UserLocalNotificationService } from '../user-local-notification/user-local-notification.service'; import { GetLoginInfoRequest } from './models/get-login-info.request'; import { GetLoginInfoResponse } from './models/get-login-info.response'; import { analyzeAndValidateNgModules } from '@angular/compiler'; import { Events } from '@ionic/angular'; import { InternationalMobileComponent } from '../../ui/mobile-number/international-mobile/international-mobile.component'; import { SMSCheckRequest } from './models/smscheck.request'; import { SMSCheckResponse } from './models/smscheck.response'; @Injectable({ providedIn: 'root' }) export class AuthenticationService { public static MOBILE_USER = 102; /* login methods */ public static loginURL = 'Services/Authentication.svc/REST/CheckPatientAuthentication'; public static checkUserAuthURL = 'Services/Authentication.svc/REST/CheckPatientAuthentication'; public static login = 'Services/ERP.svc/REST/MemberLogin'; public static activationCodeURL = 'Services/Authentication.svc/REST/CheckActivationCode'; public static getLoginInfoURL = 'Services/Authentication.svc/REST/GetMobileLoginInfo'; public static smsCheck='Services/ERP.svc/REST/CheckActivationCode'; public static smsCheckForget='Services/ERP.svc/REST/CheckPublicActivationCode'; public static smsSendCode='Services/ERP.svc/REST/SendPublicActivationCode'; /* register methods */ public static checkPatientForRegisterationURL = 'Services/Authentication.svc/REST/CheckPatientForRegisteration'; public static sendSmsForRegisterURL = 'Services/Authentication.svc/REST/SendSMSForPatientRegisteration'; public static registerTempUserURL = 'Services/Authentication.svc/REST/RegisterTempPatientWithoutSMS'; public static sendSMSForgotFileNoURL = 'Services/Authentication.svc/REST/SendPatientIDSMSByMobileNumber'; public static forgotFileIDURL = 'Services/Authentication.svc/REST/CheckActivationCodeForSendFileNo'; public static user: AuthenticatedUser; public static LOGIN_EVENT = 'user-login-event'; public static FAMILY_LOGIN_EVENT = 'family-login-event'; public static AUTHENTICATED_USER_KEY = 'save-authenticated-user'; // private static user: AuthenticatedUser; constructor( public con: ConnectorService, public cs: CommonService, public ts: TranslatorService, public nativeStorage: NativeStorage, public localNotifications: UserLocalNotificationService, private events: Events ) { } public authenticateRequest(request: Request, automaticLogin = true): Request { this.setPublicFields(request); const user = this.getAuthenticatedUser(); if (user) { /*user with eye prescriptions*/ // request.PatientID = user.PatientID; // request.TokenID = user.TokenID; // request.PatientOutSA = user.PatientOutSA ? 1 : 0; // request.PatientTypeID = user.PatientTypeID; if (AuthenticationService.requireRelogin) { this.sessionTimeOutDialog(); } } else { this.cs.userNeedToReLogin(); } /* else { if (automaticLogin) { this.cs.openUserLogin(); } } */ return request; } public setPublicFields(request: Request): Request { request.VersionID = 1; request.Channel = 31; request.LanguageID = TranslatorService.getCurrentLanguageCode(); //request.IPAdress = '10.10.10.10'; request.SessionID = 'any thing'; // ??? required for not authorized login funny request.MobileType = "android"; //request.isDentalAllowedBackend = false; return request; } public authenticateAndSetPersonalInformation(request: EmailRequest, examinationInfo?: any): EmailRequest { // if not authenticated will be redirected to login this.authenticateRequest(request); const user = this.getAuthenticatedUser(); if (user) { // request.To = user.Email; // request.DateofBirth = user.DateofBirth; // request.PatientIditificationNum = user.PatientIdentificationNo; // request.PatientMobileNumber = user.MobileNo; // request.PatientName = user.PatientName; // request.PatientOutSA = user.PatientOutSA ? 1 : 0; // request.PatientTypeID = user.PatientTypeID; if (examinationInfo) { if (examinationInfo.SetupID) { request.SetupID = examinationInfo.SetupID; } if (examinationInfo.ProjectName) { request.ProjectName = examinationInfo.ProjectName; } if (examinationInfo.ClinicName) { request.ClinicName = examinationInfo.ClinicDescription; } if (examinationInfo.DoctorName) { request.DoctorName = examinationInfo.DoctorName; } if (examinationInfo.ProjectID) { request.ProjectID = examinationInfo.ProjectID; } if (examinationInfo.InvoiceNo) { request.InvoiceNo = examinationInfo.InvoiceNo; } if (examinationInfo.OrderDate) { request.OrderDate = this.cs.getDateISO(this.cs.evaluteDateAsObject(examinationInfo.OrderDate)); } } } return request; } public getAuthenticatedRequest(login = true): Request { const request = new Request(); this.authenticateRequest(request, login); return request; } public getPublicRequest(): Request { const request = new Request(); this.setPublicFields(request); return request; } public login(request: LoginRequest, onError: any, errorLabel: string): Observable { request.PatientID = 0; request.TokenID = ''; request.isDentalAllowedBackend = false; request.isRegister = false; return this.con.post(AuthenticationService.loginURL, request, onError, errorLabel); } public isAuthenticated(): boolean { return AuthenticationService.user != null; } /** * this fucntion load from user information if he logged in before * and save user into memory and local storage * disable notifications for previous user if new user logged in with different user * * * info: * 1- user stored in local storage without token * @param result check activation code result */ public setAuthenticatedUser(result: CheckActivationCodeResponse): Observable { return Observable.create(observer => { this.loadAuthenticatedUser().subscribe((loadedUser: AuthenticatedUser) => { AuthenticationService.requireRelogin = false; this.startIdleMonitoring(); const user = this.updateAuthenticatedUser(result, loadedUser); /* we store in hd without token but with token in memory*/ this.saveUserInStorage(user).subscribe((success: boolean) => { AuthenticationService.user = user; this.publishUserChangeEvent(); observer.next(true); observer.complete(); }); }); }); } public resetAuthenticatedUser(user: AuthenticatedUser ) { AuthenticationService.user = user; AuthenticationService.requireRelogin = false; this.startIdleMonitoring(); this.publishUserChangeEvent(); } public setAuthenticatedMemberFamilyUser(result: CheckActivationCodeResponse) { const authUser = new AuthenticatedUser(); AuthenticationService.requireRelogin = false; this.startIdleMonitoring(); const user = this.updateAuthenticatedUser(result, authUser); // user["hello"]= "ok"; /* we store in hd without token but with token in memory*/ AuthenticationService.user = user; this.publishFamilyMemeberUserChangeEvent(); } public updateLoggedInUser(newUser: AuthenticatedUser): Observable { return Observable.create(observer => { /* we store in hd without token but with token in memory*/ this.saveUserInStorage(newUser).subscribe((success: boolean) => { AuthenticationService.requireRelogin = false; AuthenticationService.user = newUser; this.publishUserChangeEvent(); observer.next(success); observer.complete(); }); }); } public setDeviceToken(token: string) { const user = this.getAuthenticatedUser(); if (user) { // user.deviceToken = token; this.updateLoggedInUser(user).subscribe((success: boolean) => { // console.log('token stored:' + token); }); } } public registerTempUser(request: RegisterInformationRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); request.TokenID = ''; return this.con.post(AuthenticationService.registerTempUserURL, request, onError, errorLabel); } public getLoginInfo(request: GetLoginInfoRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); return this.con.post(AuthenticationService.getLoginInfoURL, request, onError, errorLabel); } /** *update new authenticated user from previously stored and loaded user */ private updateAuthenticatedUser(result: CheckActivationCodeResponse, loadedUser: AuthenticatedUser): AuthenticatedUser { const user = result.MemberInformationList[0]; return user; } public isRealEmail(email: string): boolean { return EmailInput.isValidEmail(email); } public isAuthenticatedUserHasRealEmail(): boolean { const user = this.getAuthenticatedUser(); return user ? this.isRealEmail(user.EMPLOYEE_EMAIL_ADDRESS) : false; } /** * we store in localstorage without token but we keep it in the static member */ private saveUserInStorage(user: AuthenticatedUser): Observable { return Observable.create(observer => { if (user) { // const TokenID = user.TokenID; // user.TokenID = null; this.nativeStorage.setItem(AuthenticationService.AUTHENTICATED_USER_KEY, user) .then( () => { //user.TokenID = TokenID; observer.next(true); observer.complete(); }, error => { // user.TokenID = TokenID; observer.next(false); observer.complete(); } ); } else { observer.next(false); observer.complete(); } }); } /** * signout *clear user session and storage information */ public clearUser(): Observable { this.clearUserSession(); return Observable.create(observer => { this.publishUserChangeEvent(); this.nativeStorage.remove(AuthenticationService.AUTHENTICATED_USER_KEY).then( () => { observer.next(true); observer.complete(); }, () => { observer.next(false); observer.complete(); }); }); } private publishUserChangeEvent() { this.events.publish(AuthenticationService.LOGIN_EVENT, this.getAuthenticatedUser(), Date.now()); } private publishFamilyMemeberUserChangeEvent() { this.events.publish(AuthenticationService.FAMILY_LOGIN_EVENT, this.getAuthenticatedUser(), Date.now()); } public clearUserSession() { AuthenticationService.user = null; } public loadAuthenticatedUser(): Observable { return Observable.create(observer => { this.nativeStorage.getItem(AuthenticationService.AUTHENTICATED_USER_KEY) .then( (user) => { observer.next(user); observer.complete(); }, error => { // this.getAuthenticatedUser() //commented for now will uncomment when we are going to deploy on the device; //observer.next(null); observer.next(AuthenticationService.user); observer.complete(); } ); }); } // TODO you should read from local storage and update static member public getAuthenticatedUser(): AuthenticatedUser { /* if (AuthenticationService.requireRelogin) { this.sessionTimeOutDialog(); return new AuthenticatedUser(); } else { return AuthenticationService.user; } */ return AuthenticationService.user; } public checkUserAuthentication(request: CheckUserAuthenticationRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); return this.con.post(AuthenticationService.login, request, onError, errorLabel); } public checkActivationCode(request: CheckActivationCodeRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); return this.con.post(AuthenticationService.activationCodeURL, request, onError, errorLabel); } public checkSMS(request: SMSCheckRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); return this.con.post(AuthenticationService.smsCheck, request, onError, errorLabel); } /* client side: id no , mobile no , zip code */ public checkPatientForRegisteration(request: CheckPatientRegisterationRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); request.TokenID = ''; request.PatientID = 0; request.isRegister = false; return this.con.post(AuthenticationService.checkPatientForRegisterationURL, request, onError, errorLabel); } /* client side: id no , mobile no , zip code */ public sendSmsForPatientRegisteration(request: CheckPatientRegisterationRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); request.TokenID = ''; request.PatientID = 0; request.isRegister = false; return this.con.post(AuthenticationService.sendSmsForRegisterURL, request, onError, errorLabel); } public checkRegisterationActivationCode(request: CheckRegisterationCodeRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); return this.con.post(AuthenticationService.activationCodeURL, request, onError, errorLabel); } public sendSMSForForgotFileNumber(request: CheckUserAuthenticationRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); request.TokenID = ''; request.PatientIdentificationID = ''; request.PatientID = 0; //request.SearchType = 2; //request.isRegister = false; return this.con.post(AuthenticationService.sendSMSForgotFileNoURL, request, onError, errorLabel); } public forgotFileIdActivation(request: CheckActivationCodeRequest, onError: any, errorLabel: string) : Observable { this.setPublicFields(request); request.TokenID = ''; request.PatientIdentificationID = ''; request.PatientID = 0; request.SearchType = 2; request.isRegister = false; return this.con.post(AuthenticationService.forgotFileIDURL, request, onError, errorLabel); } public isSAUDIIDValid(id: string): boolean { if (!id) { return false; } try { id = id.toString(); id = id.trim(); const returnValue = Number(id); let sum = 0; if (returnValue > 0) { const type = Number(id.charAt(0)); if (id.length !== 10) { return false; } if (type !== 2 && type !== 1) { return false; } for (let i = 0; i < 10; i++) { if ((i % 2) === 0) { const a = id.charAt(i); const x = Number(a) * 2; let b = x.toString(); if (b.length === 1) { b = '0' + b; } sum += Number(b.charAt(0)) + Number(b.charAt(1)); } else { sum += Number(id.charAt(i)); } } return ((sum % 10) === 0); } } catch (err) { } return false; } public checkUserHasEmailDialog(): Observable { return Observable.create(observer => { if (this.isAuthenticatedUserHasRealEmail()) { const message = this.ts.trPK('general', 'send-email') .replace('[0]', this.getAuthenticatedUser().EmailAddress); this.cs.presentConfirmDialog(message, () => { observer.next(); observer.complete(); }); } else { this.cs.presentConfirmDialog(this.ts.trPK('login', 'enter-email'), () => { //this.cs.openPatientProfile(); observer.complete(); }); } }); } /* user session timeout after idle for 20 Minute */ private static requireRelogin = false; public static monitorInterval_M = 20 * 60 * 1000; // public static monitorInterval_M = 15 * 1000; private static timerID; private stopIdleTimer() { if (AuthenticationService.timerID) { clearTimeout(AuthenticationService.timerID); } } private sessionTimeOutDialog() { this.cs.presentConfirmDialog(this.ts.trPK('general', 'idle-relogin'), () => { // this.cs.openUserLogin(); }); } /* reset and start idling interval */ private detectIdle() { this.stopIdleTimer(); AuthenticationService.timerID = setTimeout(() => { if (AuthenticationService.user) { AuthenticationService.requireRelogin = true; this.clearUserSession(); this.publishUserChangeEvent(); this.sessionTimeOutDialog(); this.cs.openHome(); } }, AuthenticationService.monitorInterval_M); } private registerDocumentEvents(events: string[], handler: any) { for (const event of events) { document.addEventListener(event, handler); } } private static eventsRegistered = false; public startIdleMonitoring() { if (!AuthenticationService.eventsRegistered) { AuthenticationService.eventsRegistered = true; // every time we have and event this.registerDocumentEvents(['mousedown', 'touchstart', 'click', 'keyup'], () => { // if user still logged in and session not expired if (AuthenticationService.user && !AuthenticationService.requireRelogin) { // reset and start idling timer this.detectIdle(); } }); } } }