import { Injectable } from '@angular/core'; import { I18nService } from 'jslib/abstractions/i18n.service'; import { DeviceType } from 'jslib/enums/deviceType'; import { EventType } from 'jslib/enums/eventType'; import { EventResponse } from 'jslib/models/response/eventResponse'; @Injectable() export class EventService { constructor(private i18nService: I18nService) { } getDefaultDateFilters() { const d = new Date(); const end = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59); d.setDate(d.getDate() - 30); const start = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0); return [this.toDateTimeLocalString(start), this.toDateTimeLocalString(end)]; } formatDateFilters(filterStart: string, filterEnd: string) { const start: Date = new Date(filterStart); const end: Date = new Date(filterEnd + ':59.999'); if (isNaN(start.getTime()) || isNaN(end.getTime()) || end < start) { throw new Error('Invalid date range.'); } return [start.toISOString(), end.toISOString()]; } getEventInfo(ev: EventResponse, options = new EventOptions()): EventInfo { const appInfo = this.getAppInfo(ev.deviceType); return { message: this.getEventMessage(ev, options), appIcon: appInfo[0], appName: appInfo[1], }; } private getEventMessage(ev: EventResponse, options: EventOptions) { let msg = ''; switch (ev.type) { // User case EventType.User_LoggedIn: msg = this.i18nService.t('loggedIn'); break; case EventType.User_ChangedPassword: msg = this.i18nService.t('changedPassword'); break; case EventType.User_Enabled2fa: msg = this.i18nService.t('enabled2fa'); break; case EventType.User_Disabled2fa: msg = this.i18nService.t('disabled2fa'); break; case EventType.User_Recovered2fa: msg = this.i18nService.t('recovered2fa'); break; case EventType.User_FailedLogIn: msg = this.i18nService.t('failedLogin'); break; case EventType.User_FailedLogIn2fa: msg = this.i18nService.t('failedLogin2fa'); break; // Cipher case EventType.Cipher_Created: msg = this.i18nService.t('createdItemId', this.formatCipherId(ev, options)); break; case EventType.Cipher_Updated: msg = this.i18nService.t('editedItemId', this.formatCipherId(ev, options)); break; case EventType.Cipher_Deleted: msg = this.i18nService.t('deletedItemId', this.formatCipherId(ev, options)); break; case EventType.Cipher_AttachmentCreated: msg = this.i18nService.t('createdAttachmentForItem', this.formatCipherId(ev, options)); break; case EventType.Cipher_AttachmentDeleted: msg = this.i18nService.t('deletedAttachmentForItem', this.formatCipherId(ev, options)); break; case EventType.Cipher_Shared: msg = this.i18nService.t('sharedItemId', this.formatCipherId(ev, options)); break; case EventType.Cipher_UpdatedCollections: msg = this.i18nService.t('editedCollectionsForItem', this.formatCipherId(ev, options)); break; // Collection case EventType.Collection_Created: msg = this.i18nService.t('createdCollectionId', this.formatCollectionId(ev)); break; case EventType.Collection_Updated: msg = this.i18nService.t('editedCollectionId', this.formatCollectionId(ev)); break; case EventType.Collection_Deleted: msg = this.i18nService.t('deletedCollectionId', this.formatCollectionId(ev)); break; // Group case EventType.Group_Created: msg = this.i18nService.t('createdGroupId', this.formatGroupId(ev)); break; case EventType.Group_Updated: msg = this.i18nService.t('editedGroupId', this.formatGroupId(ev)); break; case EventType.Group_Deleted: msg = this.i18nService.t('deletedGroupId', this.formatGroupId(ev)); break; // Org user case EventType.OrganizationUser_Invited: msg = this.i18nService.t('invitedUserId', this.formatOrgUserId(ev)); break; case EventType.OrganizationUser_Confirmed: msg = this.i18nService.t('confirmedUserId', this.formatOrgUserId(ev)); break; case EventType.OrganizationUser_Updated: msg = this.i18nService.t('editedUserId', this.formatOrgUserId(ev)); break; case EventType.OrganizationUser_Removed: msg = this.i18nService.t('removedUserId', this.formatOrgUserId(ev)); break; case EventType.OrganizationUser_UpdatedGroups: msg = this.i18nService.t('editedGroupsForUser', this.formatOrgUserId(ev)); break; // Org case EventType.Organization_Updated: msg = this.i18nService.t('editedOrgSettings'); break; default: break; } return msg === '' ? null : msg; } private getAppInfo(deviceType: DeviceType): [string, string] { switch (deviceType) { case DeviceType.Android: return ['fa-android', this.i18nService.t('mobile') + ' - Android']; case DeviceType.iOS: return ['fa-apple', this.i18nService.t('mobile') + ' - iOS']; case DeviceType.UWP: return ['fa-windows', this.i18nService.t('mobile') + ' - Windows']; case DeviceType.ChromeExtension: return ['fa-chrome', this.i18nService.t('extension') + ' - Chrome']; case DeviceType.FirefoxExtension: return ['fa-firefox', this.i18nService.t('extension') + ' - Firefox']; case DeviceType.OperaExtension: return ['fa-opera', this.i18nService.t('extension') + ' - Opera']; case DeviceType.EdgeExtension: return ['fa-edge', this.i18nService.t('extension') + ' - Edge']; case DeviceType.VivaldiExtension: return ['fa-puzzle-piece', this.i18nService.t('extension') + ' - Vivaldi']; case DeviceType.SafariExtension: return ['fa-safari', this.i18nService.t('extension') + ' - Safari']; case DeviceType.WindowsDesktop: return ['fa-windows', this.i18nService.t('desktop') + ' - Windows']; case DeviceType.MacOsDesktop: return ['fa-apple', this.i18nService.t('desktop') + ' - macOS']; case DeviceType.LinuxDesktop: return ['fa-linux', this.i18nService.t('desktop') + ' - Linux']; case DeviceType.ChromeBrowser: return ['fa-globe', this.i18nService.t('webVault') + ' - Chrome']; case DeviceType.FirefoxBrowser: return ['fa-globe', this.i18nService.t('webVault') + ' - Firefox']; case DeviceType.OperaBrowser: return ['fa-globe', this.i18nService.t('webVault') + ' - Opera']; case DeviceType.SafariBrowser: return ['fa-globe', this.i18nService.t('webVault') + ' - Safari']; case DeviceType.VivaldiBrowser: return ['fa-globe', this.i18nService.t('webVault') + ' - Vivaldi']; case DeviceType.EdgeBrowser: return ['fa-globe', this.i18nService.t('webVault') + ' - Edge']; case DeviceType.IEBrowser: return ['fa-globe', this.i18nService.t('webVault') + ' - IE']; case DeviceType.UnknownBrowser: return ['fa-globe', this.i18nService.t('webVault') + ' - ' + this.i18nService.t('unknown')]; default: return ['fa-globe', this.i18nService.t('unknown')]; } } private formatCipherId(ev: EventResponse, options: EventOptions) { const shortId = this.getShortId(ev.cipherId); if (ev.organizationId == null || !options.cipherInfo) { return '' + shortId + ''; } const a = this.makeAnchor(shortId); a.setAttribute('href', '#/organizations/' + ev.organizationId + '/vault?search=' + shortId + '&viewEvents=' + ev.cipherId); return a.outerHTML; } private formatGroupId(ev: EventResponse) { const shortId = this.getShortId(ev.groupId); const a = this.makeAnchor(shortId); a.setAttribute('href', '#/organizations/' + ev.organizationId + '/manage/groups?search=' + shortId); return a.outerHTML; } private formatCollectionId(ev: EventResponse) { const shortId = this.getShortId(ev.collectionId); const a = this.makeAnchor(shortId); a.setAttribute('href', '#/organizations/' + ev.organizationId + '/manage/collections?search=' + shortId); return a.outerHTML; } private formatOrgUserId(ev: EventResponse) { const shortId = this.getShortId(ev.organizationUserId); const a = this.makeAnchor(shortId); a.setAttribute('href', '#/organizations/' + ev.organizationId + '/manage/people?search=' + shortId); return a.outerHTML; } private makeAnchor(shortId: string) { const a = document.createElement('a'); a.title = this.i18nService.t('view'); a.innerHTML = '' + shortId + ''; return a; } private getShortId(id: string) { return id.substring(0, 8); } private toDateTimeLocalString(date: Date) { return date.getFullYear() + '-' + this.pad(date.getMonth() + 1) + '-' + this.pad(date.getDate()) + 'T' + this.pad(date.getHours()) + ':' + this.pad(date.getMinutes()); } private pad(num: number) { const norm = Math.floor(Math.abs(num)); return (norm < 10 ? '0' : '') + norm; } } export class EventInfo { message: string; appIcon: string; appName: string; } export class EventOptions { cipherInfo = true; }