mirror of
https://github.com/koush/scrypted.git
synced 2026-05-25 14:10:30 +01:00
Merge pull request #101 from IGx89/synology-ss/two-factor-auth
synology-ss: add 2FA support
This commit is contained in:
7
plugins/synology-ss/package-lock.json
generated
7
plugins/synology-ss/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/synology-ss",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/synology-ss",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"license": "Apache",
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0"
|
||||
@@ -17,7 +17,8 @@
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"version": "0.0.121",
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.0.133",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/synology-ss",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"description": "A Synology Surveillance Station plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
|
||||
@@ -36,7 +36,15 @@ export class SynologyApiClient {
|
||||
idList: cameraIds.join(',')
|
||||
};
|
||||
|
||||
return await this.sendRequest<SynologyCameraLiveViewPath[]>(params);
|
||||
const errorCodeDescs = {
|
||||
'400': 'Execution failed',
|
||||
// Usually when 401 happens, there's a "Fail to get local host Ip str!" error in surveillance.log.
|
||||
// One instance it was due to an old network bridge configured in Docker that had to be removed.
|
||||
'401': 'Parameter invalid (possibly due to misconfigured Synology network interface -- run ifconfig on your server)',
|
||||
'402': 'Camera disabled'
|
||||
};
|
||||
|
||||
return await this.sendRequest<SynologyCameraLiveViewPath[]>(params, null, false, errorCodeDescs);
|
||||
}
|
||||
|
||||
public async getCameraSnapshot(cameraId: number | string) {
|
||||
@@ -67,7 +75,8 @@ export class SynologyApiClient {
|
||||
return response.cameras;
|
||||
}
|
||||
|
||||
public async login(account: string, password: string): Promise<void> {
|
||||
public async login(account: string, password: string, otpCode?: number, enableDeviceToken: boolean = false, deviceName?: string,
|
||||
deviceId?: string): Promise<string | undefined> {
|
||||
const params = {
|
||||
api: 'SYNO.API.Auth',
|
||||
version: 6,
|
||||
@@ -77,6 +86,22 @@ export class SynologyApiClient {
|
||||
passwd: password
|
||||
};
|
||||
|
||||
if (otpCode) {
|
||||
params['otp_code'] = otpCode;
|
||||
}
|
||||
|
||||
if (enableDeviceToken) {
|
||||
params['enable_device_token'] = enableDeviceToken ? 'yes' : 'no';
|
||||
}
|
||||
|
||||
if (deviceName) {
|
||||
params['device_name'] = deviceName;
|
||||
}
|
||||
|
||||
if (deviceId) {
|
||||
params['device_id'] = deviceId;
|
||||
}
|
||||
|
||||
const errorCodeDescs = {
|
||||
'400': 'Invalid password',
|
||||
'401': 'Guest or disabled account',
|
||||
@@ -92,7 +117,9 @@ export class SynologyApiClient {
|
||||
'411': 'Account Locked (when account max try exceed)'
|
||||
};
|
||||
|
||||
await this.sendRequest(params, null, true, errorCodeDescs);
|
||||
const response = await this.sendRequest<SynologyApiAuthResponse>(params, null, true, errorCodeDescs);
|
||||
|
||||
return response.did;
|
||||
}
|
||||
|
||||
private async queryApiInfo(): Promise<Record<string, SynologyApiInfo>> {
|
||||
@@ -153,6 +180,11 @@ interface SynologyApiResponse<T> {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
interface SynologyApiAuthResponse {
|
||||
sid?: string;
|
||||
did?: string;
|
||||
}
|
||||
|
||||
interface SynologyApiListCamerasResponse {
|
||||
total: number;
|
||||
cameras: SynologyCamera[];
|
||||
|
||||
@@ -211,6 +211,8 @@ class SynologySurveillanceStation extends ScryptedDeviceBase implements Settings
|
||||
const url = this.getSetting('url');
|
||||
const username = this.getSetting('username');
|
||||
const password = this.getSetting('password');
|
||||
const otpCode = this.getSetting('otpCode');
|
||||
const mfaDeviceId = this.getSetting('mfaDeviceId');
|
||||
|
||||
this.log.clearAlerts();
|
||||
|
||||
@@ -234,8 +236,31 @@ class SynologySurveillanceStation extends ScryptedDeviceBase implements Settings
|
||||
}
|
||||
|
||||
try {
|
||||
await this.api.login(username, password);
|
||||
const newMfaDeviceId = await this.api.login(username, password, otpCode ? parseInt(otpCode) : undefined, !!otpCode, 'Scrypted', mfaDeviceId);
|
||||
|
||||
// If a OTP was present, store the device ID to allow us to skip the OTP requirement next login.
|
||||
if (otpCode) {
|
||||
this.storage.setItem('mfaDeviceId', newMfaDeviceId);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.log.a(`login error: ${e}`);
|
||||
this.console.error('login error', e);
|
||||
|
||||
// Clear device ID upon login failure, since it's likely useless now
|
||||
this.storage.removeItem('mfaDeviceId');
|
||||
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
// Clear the OTP setting if provided since it's a temporary code
|
||||
if (otpCode) {
|
||||
this.storage.removeItem('otpCode');
|
||||
this.onDeviceEvent(ScryptedInterface.Settings, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.cameras = await this.api.listCameras();
|
||||
|
||||
if (!this.cameras) {
|
||||
@@ -289,8 +314,8 @@ class SynologySurveillanceStation extends ScryptedDeviceBase implements Settings
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.log.a(`login error: ${e}`);
|
||||
this.console.error('login error', e);
|
||||
this.log.a(`device discovery error: ${e}`);
|
||||
this.console.error('device discovery error', e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,6 +348,13 @@ class SynologySurveillanceStation extends ScryptedDeviceBase implements Settings
|
||||
type: 'password',
|
||||
value: this.getSetting('password'),
|
||||
},
|
||||
{
|
||||
key: 'otpCode',
|
||||
title: 'Verification Code (OTP)',
|
||||
description: 'Required only if you have two-factor authentication enabled',
|
||||
type: 'integer',
|
||||
value: this.getSetting('otpCode'),
|
||||
},
|
||||
{
|
||||
key: 'url',
|
||||
title: 'Synology Surveillance Station URL',
|
||||
|
||||
Reference in New Issue
Block a user