bitwarden-estensione-browser/src/services/notifications.service.ts

170 lines
6.0 KiB
TypeScript
Raw Normal View History

2018-08-20 19:45:32 +02:00
import * as signalR from '@aspnet/signalr';
import { NotificationType } from '../enums/notificationType';
import { ApiService } from '../abstractions/api.service';
2018-08-20 22:01:26 +02:00
import { AppIdService } from '../abstractions/appId.service';
import { CryptoService } from '../abstractions/crypto.service';
2018-08-20 19:45:32 +02:00
import { EnvironmentService } from '../abstractions/environment.service';
import { NotificationsService as NotificationsServiceAbstraction } from '../abstractions/notifications.service';
import { SyncService } from '../abstractions/sync.service';
import { UserService } from '../abstractions/user.service';
2018-08-20 22:01:26 +02:00
import {
NotificationResponse,
SyncCipherNotification,
SyncFolderNotification,
} from '../models/response/notificationResponse';
2018-08-20 19:45:32 +02:00
export class NotificationsService implements NotificationsServiceAbstraction {
private signalrConnection: signalR.HubConnection;
private url: string;
private connected = false;
private inited = false;
2018-08-23 03:09:58 +02:00
private inactive = false;
private reconnectTimer: any = null;
2018-08-20 19:45:32 +02:00
constructor(private userService: UserService, private syncService: SyncService,
private appIdService: AppIdService, private apiService: ApiService,
private cryptoService: CryptoService) { }
2018-08-20 19:45:32 +02:00
async init(environmentService: EnvironmentService): Promise<void> {
this.inited = false;
this.url = 'https://notifications.bitwarden.com';
2018-08-20 19:45:32 +02:00
if (environmentService.notificationsUrl != null) {
this.url = environmentService.notificationsUrl;
2018-08-20 19:45:32 +02:00
} else if (environmentService.baseUrl != null) {
this.url = environmentService.baseUrl + '/notifications';
2018-08-20 19:45:32 +02:00
}
if (this.signalrConnection != null) {
this.signalrConnection.off('ReceiveMessage');
await this.signalrConnection.stop();
this.connected = false;
this.signalrConnection = null;
}
this.signalrConnection = new signalR.HubConnectionBuilder()
.withUrl(this.url + '/hub', {
accessTokenFactory: () => this.apiService.getActiveBearerToken(),
})
// .configureLogging(signalR.LogLevel.Information)
.build();
2018-08-22 19:48:51 +02:00
this.signalrConnection.on('ReceiveMessage',
(data: any) => this.processNotification(new NotificationResponse(data)));
this.signalrConnection.onclose(() => {
this.connected = false;
this.reconnect();
});
this.inited = true;
if (await this.isAuthedAndUnlocked()) {
await this.connect();
}
2018-08-20 19:45:32 +02:00
}
async updateConnection(): Promise<void> {
try {
if (await this.isAuthedAndUnlocked()) {
await this.connect();
2018-08-20 19:45:32 +02:00
} else {
await this.signalrConnection.stop();
}
} catch (e) {
// tslint:disable-next-line
console.error(e.toString());
}
}
async reconnectFromActivity(): Promise<void> {
2018-08-23 03:09:58 +02:00
this.inactive = false;
if (!this.connected) {
if (await this.isAuthedAndUnlocked()) {
await this.reconnect();
await this.syncService.fullSync(false);
2018-08-23 03:09:58 +02:00
}
}
}
async disconnectFromInactivity(): Promise<void> {
2018-08-23 03:09:58 +02:00
this.inactive = true;
if (this.connected) {
await this.signalrConnection.stop();
}
}
2018-08-20 19:45:32 +02:00
private async processNotification(notification: NotificationResponse) {
2018-08-20 22:01:26 +02:00
const appId = await this.appIdService.getAppId();
if (notification == null || notification.contextId === appId) {
2018-08-20 19:45:32 +02:00
return;
}
switch (notification.type) {
case NotificationType.SyncCipherCreate:
case NotificationType.SyncCipherUpdate:
await this.syncService.syncUpsertCipher(notification.payload as SyncCipherNotification,
notification.type === NotificationType.SyncCipherUpdate);
2018-08-20 22:01:26 +02:00
break;
case NotificationType.SyncCipherDelete:
2018-08-20 19:45:32 +02:00
case NotificationType.SyncLoginDelete:
await this.syncService.syncDeleteCipher(notification.payload as SyncCipherNotification);
2018-08-20 19:45:32 +02:00
break;
case NotificationType.SyncFolderCreate:
case NotificationType.SyncFolderUpdate:
await this.syncService.syncUpsertFolder(notification.payload as SyncFolderNotification,
notification.type === NotificationType.SyncFolderUpdate);
2018-08-20 22:01:26 +02:00
break;
case NotificationType.SyncFolderDelete:
await this.syncService.syncDeleteFolder(notification.payload as SyncFolderNotification);
2018-08-20 19:45:32 +02:00
break;
case NotificationType.SyncVault:
case NotificationType.SyncCiphers:
case NotificationType.SyncSettings:
await this.syncService.fullSync(false);
break;
case NotificationType.SyncOrgKeys:
await this.apiService.refreshIdentityToken();
await this.syncService.fullSync(true);
// Stop so a reconnect can be made
await this.signalrConnection.stop();
2018-08-20 19:45:32 +02:00
break;
default:
break;
}
}
private async connect() {
await this.signalrConnection.start();
this.connected = true;
}
private async reconnect() {
if (this.reconnectTimer != null) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
2018-08-23 03:09:58 +02:00
if (this.connected || !this.inited || this.inactive) {
return;
}
const authedAndUnlocked = await this.isAuthedAndUnlocked();
if (!authedAndUnlocked) {
return;
}
try {
await this.connect();
} catch { }
if (!this.connected) {
2018-08-22 19:48:51 +02:00
this.reconnectTimer = setTimeout(() => this.reconnect(), 120000);
}
}
private async isAuthedAndUnlocked() {
if (await this.userService.isAuthenticated()) {
return this.cryptoService.hasKey();
}
return false;
}
2018-08-20 19:45:32 +02:00
}