fix(device): check privilege on Linux (#519)
* fix(device): check privilege on Linux * device connected if also have permission * fix rules sh path * refactor permission detection * fic hasPermission condition * fix return value
This commit is contained in:
committed by
László Monda
parent
969c36561b
commit
c11658f1fc
@@ -31,7 +31,7 @@ export class AppService extends MainServiceBase {
|
||||
addons: this.options.addons || false,
|
||||
autoWriteConfig: this.options['auto-write-config'] || false
|
||||
},
|
||||
deviceConnected: this.deviceService.isConnected,
|
||||
deviceConnected: this.uhkHidDeviceService.deviceConnected(),
|
||||
hasPermission: this.uhkHidDeviceService.hasPermission(),
|
||||
agentVersionInfo: {
|
||||
version: packageJson.version,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { ConfigurationReply, IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
||||
import { Constants, snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||
import { ConfigurationReply, DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
||||
import { snooze, UhkHidDevice, UhkOperations } from 'uhk-usb';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { Device, devices } from 'node-hid';
|
||||
import { emptyDir } from 'fs-extra';
|
||||
|
||||
import 'rxjs/add/observable/interval';
|
||||
@@ -19,13 +18,11 @@ import { QueueManager } from './queue-manager';
|
||||
/**
|
||||
* IpcMain pair of the UHK Communication
|
||||
* Functionality:
|
||||
* - Detect device is connected or not
|
||||
* - Send UserConfiguration to the UHK Device
|
||||
* - Read UserConfiguration from the UHK Device
|
||||
*/
|
||||
export class DeviceService {
|
||||
private pollTimer$: Subscription;
|
||||
private connected = false;
|
||||
private queueManager = new QueueManager();
|
||||
|
||||
constructor(private logService: LogService,
|
||||
@@ -66,14 +63,6 @@ export class DeviceService {
|
||||
logService.debug('[DeviceService] init success');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with true is an UHK Device is connected to the computer.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public get isConnected(): boolean {
|
||||
return this.connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with the actual UserConfiguration from UHK Device
|
||||
* @returns {Promise<Buffer>}
|
||||
@@ -102,7 +91,6 @@ export class DeviceService {
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.connected = false;
|
||||
this.stopPollTimer();
|
||||
this.logService.info('[DeviceService] Device connection checker stopped.');
|
||||
}
|
||||
@@ -153,15 +141,16 @@ export class DeviceService {
|
||||
|
||||
this.pollTimer$ = Observable.interval(1000)
|
||||
.startWith(0)
|
||||
.map(() => {
|
||||
return devices().some((dev: Device) => dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.PRODUCT_ID);
|
||||
})
|
||||
.map(() => this.device.deviceConnected())
|
||||
.distinctUntilChanged()
|
||||
.do((connected: boolean) => {
|
||||
this.connected = connected;
|
||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, connected);
|
||||
this.logService.info(`[DeviceService] Device connection state changed to: ${connected}`);
|
||||
const response: DeviceConnectionState = {
|
||||
connected,
|
||||
hasPermission: this.device.hasPermission()
|
||||
};
|
||||
|
||||
this.win.webContents.send(IpcEvents.device.deviceConnectionStateChanged, response);
|
||||
this.logService.info('[DeviceService] Device connection state changed to:', response);
|
||||
})
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export class SudoService {
|
||||
|
||||
constructor(private logService: LogService) {
|
||||
if (isDev) {
|
||||
this.rootDir = path.join(path.join(process.cwd(), process.argv[1]), '..');
|
||||
this.rootDir = path.join(path.join(process.cwd(), process.argv[1]), '../../../../');
|
||||
} else {
|
||||
this.rootDir = path.dirname(app.getAppPath());
|
||||
}
|
||||
@@ -40,11 +40,12 @@ export class SudoService {
|
||||
name: 'Setting UHK access rules'
|
||||
};
|
||||
const command = `sh ${scriptPath}`;
|
||||
console.log(command);
|
||||
this.logService.debug('[SudoService] Set privilege command: ', command);
|
||||
sudo.exec(command, options, (error: any) => {
|
||||
const response = new IpcResponse();
|
||||
|
||||
if (error) {
|
||||
this.logService.error('[SudoService] Error when set privilege: ', error);
|
||||
response.success = false;
|
||||
response.error = error;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface DeviceConnectionState {
|
||||
connected: boolean;
|
||||
hasPermission: boolean;
|
||||
}
|
||||
@@ -4,3 +4,4 @@ export * from './ipc-response';
|
||||
export * from './app-start-info';
|
||||
export * from './configuration-reply';
|
||||
export * from './version-information';
|
||||
export * from './device-connection-state';
|
||||
|
||||
@@ -25,6 +25,7 @@ export class UhkHidDevice {
|
||||
* @private
|
||||
*/
|
||||
private _device: HID;
|
||||
private _hasPermission = false;
|
||||
|
||||
constructor(private logService: LogService) {
|
||||
}
|
||||
@@ -38,8 +39,18 @@ export class UhkHidDevice {
|
||||
*/
|
||||
public hasPermission(): boolean {
|
||||
try {
|
||||
devices();
|
||||
return true;
|
||||
if (this._hasPermission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.deviceConnected()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this._hasPermission = this.getDevice() !== null;
|
||||
this.close();
|
||||
|
||||
return this._hasPermission;
|
||||
} catch (err) {
|
||||
this.logService.error('[UhkHidDevice] hasPermission', err);
|
||||
}
|
||||
@@ -47,6 +58,21 @@ export class UhkHidDevice {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return with true is an UHK Device is connected to the computer.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public deviceConnected(): boolean {
|
||||
const connected = devices().some((dev: Device) => dev.vendorId === Constants.VENDOR_ID &&
|
||||
dev.productId === Constants.PRODUCT_ID);
|
||||
|
||||
if (!connected) {
|
||||
this._hasPermission = false;
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to the UHK device and wait for the response.
|
||||
* Throw an error when 1st byte of the response is not 0
|
||||
@@ -225,7 +251,7 @@ export class UhkHidDevice {
|
||||
}
|
||||
}
|
||||
|
||||
function kbootKommandName(module: ModuleSlotToI2cAddress): string {
|
||||
function kbootKommandName(module: ModuleSlotToI2cAddress): string {
|
||||
switch (module) {
|
||||
case ModuleSlotToI2cAddress.leftHalf:
|
||||
return 'leftHalf';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { Action, Store } from '@ngrx/store';
|
||||
|
||||
import { IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
||||
import { DeviceConnectionState, IpcEvents, IpcResponse, LogService } from 'uhk-common';
|
||||
import { AppState } from '../store';
|
||||
import { IpcCommonRenderer } from './ipc-common-renderer';
|
||||
import {
|
||||
@@ -47,7 +47,7 @@ export class DeviceRendererService {
|
||||
}
|
||||
|
||||
private registerEvents(): void {
|
||||
this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: boolean) => {
|
||||
this.ipcRenderer.on(IpcEvents.device.deviceConnectionStateChanged, (event: string, arg: DeviceConnectionState) => {
|
||||
this.dispachStoreAction(new ConnectionStateChangedAction(arg));
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { IpcResponse, type } from 'uhk-common';
|
||||
import { DeviceConnectionState, IpcResponse, type } from 'uhk-common';
|
||||
|
||||
const PREFIX = '[device] ';
|
||||
|
||||
@@ -8,7 +8,6 @@ export const ActionTypes = {
|
||||
SET_PRIVILEGE_ON_LINUX: type(PREFIX + 'set privilege on linux'),
|
||||
SET_PRIVILEGE_ON_LINUX_REPLY: type(PREFIX + 'set privilege on linux reply'),
|
||||
CONNECTION_STATE_CHANGED: type(PREFIX + 'connection state changed'),
|
||||
PERMISSION_STATE_CHANGED: type(PREFIX + 'permission state changed'),
|
||||
SAVE_CONFIGURATION: type(PREFIX + 'save configuration'),
|
||||
SAVE_CONFIGURATION_REPLY: type(PREFIX + 'save configuration reply'),
|
||||
SAVING_CONFIGURATION: type(PREFIX + 'saving configuration'),
|
||||
@@ -39,14 +38,7 @@ export class SetPrivilegeOnLinuxReplyAction implements Action {
|
||||
export class ConnectionStateChangedAction implements Action {
|
||||
type = ActionTypes.CONNECTION_STATE_CHANGED;
|
||||
|
||||
constructor(public payload: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
export class PermissionStateChangedAction implements Action {
|
||||
type = ActionTypes.PERMISSION_STATE_CHANGED;
|
||||
|
||||
constructor(public payload: boolean) {
|
||||
constructor(public payload: DeviceConnectionState) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +113,6 @@ export type Actions
|
||||
= SetPrivilegeOnLinuxAction
|
||||
| SetPrivilegeOnLinuxReplyAction
|
||||
| ConnectionStateChangedAction
|
||||
| PermissionStateChangedAction
|
||||
| ShowSaveToKeyboardButtonAction
|
||||
| SaveConfigurationAction
|
||||
| SaveConfigurationReplyAction
|
||||
|
||||
@@ -26,7 +26,6 @@ import { AppUpdateRendererService } from '../../services/app-update-renderer.ser
|
||||
import {
|
||||
ActionTypes as DeviceActions,
|
||||
ConnectionStateChangedAction,
|
||||
PermissionStateChangedAction,
|
||||
SaveToKeyboardSuccessAction
|
||||
} from '../actions/device';
|
||||
import { AppState, autoWriteUserConfiguration } from '../index';
|
||||
@@ -64,8 +63,10 @@ export class ApplicationEffects {
|
||||
this.logService.debug('[AppEffect][processStartInfo] payload:', appInfo);
|
||||
return [
|
||||
new ApplyCommandLineArgsAction(appInfo.commandLineArgs),
|
||||
new ConnectionStateChangedAction(appInfo.deviceConnected),
|
||||
new PermissionStateChangedAction(appInfo.hasPermission),
|
||||
new ConnectionStateChangedAction({
|
||||
connected: appInfo.deviceConnected,
|
||||
hasPermission: appInfo.hasPermission
|
||||
}),
|
||||
new UpdateAgentVersionInformationAction(appInfo.agentVersionInfo)
|
||||
];
|
||||
});
|
||||
|
||||
@@ -13,12 +13,11 @@ import 'rxjs/add/operator/mergeMap';
|
||||
import 'rxjs/add/operator/withLatestFrom';
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
|
||||
import { IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common';
|
||||
import { DeviceConnectionState, IpcResponse, NotificationType, UhkBuffer, UserConfiguration } from 'uhk-common';
|
||||
import {
|
||||
ActionTypes,
|
||||
ConnectionStateChangedAction,
|
||||
HideSaveToKeyboardButton,
|
||||
PermissionStateChangedAction,
|
||||
SaveConfigurationAction,
|
||||
SaveConfigurationReplyAction,
|
||||
SaveToKeyboardSuccessAction,
|
||||
@@ -47,35 +46,25 @@ export class DeviceEffects {
|
||||
deviceConnectionStateChange$: Observable<Action> = this.actions$
|
||||
.ofType<ConnectionStateChangedAction>(ActionTypes.CONNECTION_STATE_CHANGED)
|
||||
.map(action => action.payload)
|
||||
.do((connected: boolean) => {
|
||||
if (connected) {
|
||||
.do((state: DeviceConnectionState) => {
|
||||
if (!state.hasPermission) {
|
||||
this.router.navigate(['/privilege']);
|
||||
}
|
||||
else if (state.connected) {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
else {
|
||||
this.router.navigate(['/detection']);
|
||||
}
|
||||
})
|
||||
.switchMap((connected: boolean) => {
|
||||
if (connected) {
|
||||
.switchMap((state: DeviceConnectionState) => {
|
||||
if (state.connected && state.hasPermission) {
|
||||
return Observable.of(new LoadConfigFromDeviceAction());
|
||||
}
|
||||
|
||||
return Observable.empty();
|
||||
});
|
||||
|
||||
@Effect({dispatch: false})
|
||||
permissionStateChange$: Observable<boolean> = this.actions$
|
||||
.ofType<PermissionStateChangedAction>(ActionTypes.PERMISSION_STATE_CHANGED)
|
||||
.map(action => action.payload)
|
||||
.do((hasPermission: boolean) => {
|
||||
if (hasPermission) {
|
||||
this.router.navigate(['/detection']);
|
||||
}
|
||||
else {
|
||||
this.router.navigate(['/privilege']);
|
||||
}
|
||||
});
|
||||
|
||||
@Effect({dispatch: false})
|
||||
setPrivilegeOnLinux$: Observable<Action> = this.actions$
|
||||
.ofType(ActionTypes.SET_PRIVILEGE_ON_LINUX)
|
||||
@@ -90,8 +79,10 @@ export class DeviceEffects {
|
||||
.mergeMap((response: any) => {
|
||||
if (response.success) {
|
||||
return [
|
||||
new ConnectionStateChangedAction(true),
|
||||
new PermissionStateChangedAction(true)
|
||||
new ConnectionStateChangedAction({
|
||||
connected: true,
|
||||
hasPermission: true
|
||||
})
|
||||
];
|
||||
}
|
||||
return [
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Action } from '@ngrx/store';
|
||||
import {
|
||||
ActionTypes,
|
||||
ConnectionStateChangedAction,
|
||||
PermissionStateChangedAction,
|
||||
SaveConfigurationAction, UpdateFirmwareFailedAction
|
||||
SaveConfigurationAction,
|
||||
UpdateFirmwareFailedAction
|
||||
} from '../actions/device';
|
||||
import { ActionTypes as AppActions, ElectronMainLogReceivedAction } from '../actions/app';
|
||||
import { initProgressButtonState, ProgressButtonState } from './progress-button-state';
|
||||
@@ -30,17 +30,14 @@ export const initialState: State = {
|
||||
|
||||
export function reducer(state = initialState, action: Action) {
|
||||
switch (action.type) {
|
||||
case ActionTypes.CONNECTION_STATE_CHANGED:
|
||||
case ActionTypes.CONNECTION_STATE_CHANGED: {
|
||||
const data = (<ConnectionStateChangedAction>action).payload;
|
||||
return {
|
||||
...state,
|
||||
connected: (<ConnectionStateChangedAction>action).payload
|
||||
};
|
||||
|
||||
case ActionTypes.PERMISSION_STATE_CHANGED:
|
||||
return {
|
||||
...state,
|
||||
hasPermission: (<PermissionStateChangedAction>action).payload
|
||||
connected: data.connected,
|
||||
hasPermission: data.hasPermission
|
||||
};
|
||||
}
|
||||
|
||||
case ActionTypes.SAVING_CONFIGURATION: {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user