From 23ff30a1ed7797d2569b245a07ba017db8c021fc Mon Sep 17 00:00:00 2001 From: Jonas Lochmann Date: Mon, 5 Aug 2019 00:00:00 +0000 Subject: [PATCH] Save manipulation details --- src/action/ignoremanipulation.ts | 22 +++++++++-- src/api/validator.ts | 3 ++ src/database/device.ts | 31 ++++++++++++++- .../20190805-add-had-manipulation-flags.ts | 39 +++++++++++++++++++ src/function/device/prepare-device-entry.ts | 1 + .../updatedevicestatus.ts | 8 +++- .../ignoremanipulation.ts | 10 +++++ src/function/sync/get-server-data-status.ts | 1 + src/object/serverdatastatus.ts | 1 + 9 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 src/database/migration/migrations/20190805-add-had-manipulation-flags.ts diff --git a/src/action/ignoremanipulation.ts b/src/action/ignoremanipulation.ts index 7b10259..0db329a 100644 --- a/src/action/ignoremanipulation.ts +++ b/src/action/ignoremanipulation.ts @@ -15,6 +15,7 @@ * along with this program. If not, see . */ +import { DeviceHadManipulationFlags } from '../database/device' import { assertIdWithinFamily } from '../util/token' import { ParentAction } from './basetypes' @@ -29,11 +30,12 @@ export class IgnoreManipulationAction extends ParentAction { readonly ignoreAccessibilityServiceManipulation: boolean readonly ignoreDidReboot: boolean readonly ignoreHadManipulation: boolean + readonly ignoreHadManipulationFlags: number constructor ({ deviceId, ignoreDeviceAdminManipulation, ignoreDeviceAdminManipulationAttempt, ignoreAppDowngrade, ignoreNotificationAccessManipulation, ignoreUsageStatsAccessManipulation, - ignoreOverlayPermissionManipulation, ignoreAccessibilityServiceManipulation, ignoreDidReboot, ignoreHadManipulation + ignoreOverlayPermissionManipulation, ignoreAccessibilityServiceManipulation, ignoreDidReboot, ignoreHadManipulation, ignoreHadManipulationFlags }: { deviceId: string ignoreDeviceAdminManipulation: boolean @@ -45,11 +47,19 @@ export class IgnoreManipulationAction extends ParentAction { ignoreAccessibilityServiceManipulation: boolean ignoreDidReboot: boolean ignoreHadManipulation: boolean + ignoreHadManipulationFlags: number }) { super() assertIdWithinFamily(deviceId) + if ( + (!Number.isSafeInteger(ignoreHadManipulationFlags)) || + ignoreHadManipulationFlags < 0 || ignoreHadManipulationFlags > DeviceHadManipulationFlags.ALL + ) { + throw new Error('invalid ignoreHadManipulationFlags') + } + this.deviceId = deviceId this.ignoreDeviceAdminManipulation = ignoreDeviceAdminManipulation this.ignoreDeviceAdminManipulationAttempt = ignoreDeviceAdminManipulationAttempt @@ -60,6 +70,7 @@ export class IgnoreManipulationAction extends ParentAction { this.ignoreAccessibilityServiceManipulation = ignoreAccessibilityServiceManipulation this.ignoreDidReboot = ignoreDidReboot this.ignoreHadManipulation = ignoreHadManipulation + this.ignoreHadManipulationFlags = ignoreHadManipulationFlags } serialize = (): SerializedIgnoreManipulationAction => ({ @@ -72,10 +83,11 @@ export class IgnoreManipulationAction extends ParentAction { overlay: this.ignoreOverlayPermissionManipulation, accessibilityService: this.ignoreAccessibilityServiceManipulation, usageStats: this.ignoreUsageStatsAccessManipulation, - hadManipulation: this.ignoreHadManipulation + hadManipulation: this.ignoreHadManipulation, + ignoreHadManipulationFlags: this.ignoreHadManipulationFlags }) - static parse = ({ deviceId, admin, adminA, downgrade, notification, usageStats, overlay, accessibilityService, reboot, hadManipulation }: SerializedIgnoreManipulationAction) => ( + static parse = ({ deviceId, admin, adminA, downgrade, notification, usageStats, overlay, accessibilityService, reboot, hadManipulation, ignoreHadManipulationFlags }: SerializedIgnoreManipulationAction) => ( new IgnoreManipulationAction({ deviceId, ignoreDeviceAdminManipulation: admin, @@ -86,7 +98,8 @@ export class IgnoreManipulationAction extends ParentAction { ignoreOverlayPermissionManipulation: !!overlay, ignoreAccessibilityServiceManipulation: !!accessibilityService, ignoreDidReboot: !!reboot, - ignoreHadManipulation: hadManipulation + ignoreHadManipulation: hadManipulation, + ignoreHadManipulationFlags: ignoreHadManipulationFlags || 0 }) ) } @@ -104,4 +117,5 @@ export interface SerializedIgnoreManipulationAction { reboot?: boolean overlay?: boolean accessibilityService?: boolean + ignoreHadManipulationFlags?: number } diff --git a/src/api/validator.ts b/src/api/validator.ts index d158389..7f4fbb8 100644 --- a/src/api/validator.ts +++ b/src/api/validator.ts @@ -339,6 +339,9 @@ const definitions = { }, "accessibilityService": { "type": "boolean" + }, + "ignoreHadManipulationFlags": { + "type": "number" } }, "additionalProperties": false, diff --git a/src/database/device.ts b/src/database/device.ts index 3b5ad9b..ba299ec 100644 --- a/src/database/device.ts +++ b/src/database/device.ts @@ -22,6 +22,16 @@ import { RuntimePermissionStatus, runtimePermissionStatusValues } from '../model import { authTokenColumn, booleanColumn, createEnumColumn, familyIdColumn, idWithinFamilyColumn, labelColumn, optionalIdWithinFamilyColumn, timestampColumn, versionColumn } from './columns' import { SequelizeAttributes } from './types' +export const DeviceHadManipulationFlags = { + ProtectionLevel: 1, + UsageStatsAccess: 2, + NotificationAccess: 4, + AppVersion: 8, + OverlayPermission: 16, + AccessibiityService: 32, + ALL: 1 | 2 | 4 | 8 | 16 | 32 +} + export interface DeviceAttributesVersion1 { familyId: string deviceId: string @@ -88,10 +98,14 @@ export interface DeviceAttributesVersion10 { isQorLater: boolean } +export interface DeviceAttributesVersion11 { + hadManipulationFlags: number +} + export type DeviceAttributes = DeviceAttributesVersion1 & DeviceAttributesVersion2 & DeviceAttributesVersion3 & DeviceAttributesVersion4 & DeviceAttributesVersion5 & DeviceAttributesVersion6 & DeviceAttributesVersion7 & DeviceAttributesVersion8 & - DeviceAttributesVersion9 & DeviceAttributesVersion10 + DeviceAttributesVersion9 & DeviceAttributesVersion10 & DeviceAttributesVersion11 export type DeviceModel = Sequelize.Model & DeviceAttributes export type DeviceModelStatic = typeof Sequelize.Model & { @@ -233,6 +247,18 @@ export const attributesVersion10: SequelizeAttributes } } +export const attributesVersion11: SequelizeAttributes = { + hadManipulationFlags: { + type: Sequelize.INTEGER, + allowNull: false, + defaultValue: 0, + validate: { + min: 0, + max: DeviceHadManipulationFlags.ALL + } + } +} + export const attributes: SequelizeAttributes = { ...attributesVersion1, ...attributesVersion2, @@ -243,7 +269,8 @@ export const attributes: SequelizeAttributes = { ...attributesVersion7, ...attributesVersion8, ...attributesVersion9, - ...attributesVersion10 + ...attributesVersion10, + ...attributesVersion11 } export const createDeviceModel = (sequelize: Sequelize.Sequelize): DeviceModelStatic => sequelize.define('Device', attributes) as DeviceModelStatic diff --git a/src/database/migration/migrations/20190805-add-had-manipulation-flags.ts b/src/database/migration/migrations/20190805-add-had-manipulation-flags.ts new file mode 100644 index 0000000..6c64a30 --- /dev/null +++ b/src/database/migration/migrations/20190805-add-had-manipulation-flags.ts @@ -0,0 +1,39 @@ +/* + * server component for the TimeLimit App + * Copyright (C) 2019 Jonas Lochmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { QueryInterface, Sequelize, Transaction } from 'sequelize' +import { attributesVersion11 as deviceAttributes } from '../../device' + +export async function up (queryInterface: QueryInterface, sequelize: Sequelize) { + await sequelize.transaction({ + type: Transaction.TYPES.EXCLUSIVE + }, async (transaction) => { + await queryInterface.addColumn('Devices', 'hadManipulationFlags', { + ...deviceAttributes.hadManipulationFlags + }, { + transaction + }) + }) +} + +export async function down (queryInterface: QueryInterface, sequelize: Sequelize) { + await sequelize.transaction({ + type: Transaction.TYPES.EXCLUSIVE + }, async (transaction) => { + await queryInterface.removeColumn('Devices', 'hadManipulationFlags', { transaction }) + }) +} diff --git a/src/function/device/prepare-device-entry.ts b/src/function/device/prepare-device-entry.ts index 36b35a5..e0c9edc 100644 --- a/src/function/device/prepare-device-entry.ts +++ b/src/function/device/prepare-device-entry.ts @@ -48,6 +48,7 @@ export const prepareDeviceEntry = ({ familyId, userId, deviceAuthToken, deviceId triedDisablingDeviceAdmin: false, didReboot: false, hadManipulation: false, + hadManipulationFlags: 0, lastConnectivity: '0', notSeenForLongTime: false, didDeviceReportUninstall: false, diff --git a/src/function/sync/apply-actions/dispatch-app-logic-action/updatedevicestatus.ts b/src/function/sync/apply-actions/dispatch-app-logic-action/updatedevicestatus.ts index 444c345..520ea0c 100644 --- a/src/function/sync/apply-actions/dispatch-app-logic-action/updatedevicestatus.ts +++ b/src/function/sync/apply-actions/dispatch-app-logic-action/updatedevicestatus.ts @@ -16,7 +16,7 @@ */ import { UpdateDeviceStatusAction } from '../../../../action' -import { hasDeviceManipulation } from '../../../../database/device' +import { DeviceHadManipulationFlags, hasDeviceManipulation } from '../../../../database/device' import { newPermissionStatusValues } from '../../../../model/newpermissionstatus' import { protetionLevels } from '../../../../model/protectionlevel' import { runtimePermissionStatusValues } from '../../../../model/runtimepermissionstatus' @@ -56,6 +56,7 @@ export async function dispatchUpdateDeviceStatus ({ deviceId, action, cache }: { if (hasChanged && (deviceEntry.currentProtectionLevel !== deviceEntry.highestProtectionLevel)) { deviceEntry.hadManipulation = true + deviceEntry.hadManipulationFlags |= DeviceHadManipulationFlags.ProtectionLevel } } @@ -72,6 +73,7 @@ export async function dispatchUpdateDeviceStatus ({ deviceId, action, cache }: { if (hasChanged && (deviceEntry.currentUsageStatsPermission !== deviceEntry.highestUsageStatsPermission)) { deviceEntry.hadManipulation = true + deviceEntry.hadManipulationFlags |= DeviceHadManipulationFlags.UsageStatsAccess } } @@ -88,6 +90,7 @@ export async function dispatchUpdateDeviceStatus ({ deviceId, action, cache }: { if (hasChanged && (deviceEntry.currentNotificationAccessPermission !== deviceEntry.highestNotificationAccessPermission)) { deviceEntry.hadManipulation = true + deviceEntry.hadManipulationFlags |= DeviceHadManipulationFlags.NotificationAccess } } @@ -104,6 +107,7 @@ export async function dispatchUpdateDeviceStatus ({ deviceId, action, cache }: { if (hasChanged && (deviceEntry.currentOverlayPermission !== deviceEntry.highestOverlayPermission)) { deviceEntry.hadManipulation = true + deviceEntry.hadManipulationFlags |= DeviceHadManipulationFlags.OverlayPermission } } @@ -118,6 +122,7 @@ export async function dispatchUpdateDeviceStatus ({ deviceId, action, cache }: { if (hasChanged && (deviceEntry.asEnabled !== deviceEntry.wasAsEnabled)) { deviceEntry.hadManipulation = true + deviceEntry.hadManipulationFlags |= DeviceHadManipulationFlags.AccessibiityService } } @@ -133,6 +138,7 @@ export async function dispatchUpdateDeviceStatus ({ deviceId, action, cache }: { if (hasChanged && (deviceEntry.currentAppVersion !== deviceEntry.highestAppVersion)) { deviceEntry.hadManipulation = true + deviceEntry.hadManipulationFlags |= DeviceHadManipulationFlags.AppVersion } } diff --git a/src/function/sync/apply-actions/dispatch-parent-action/ignoremanipulation.ts b/src/function/sync/apply-actions/dispatch-parent-action/ignoremanipulation.ts index 10b35fd..b16a646 100644 --- a/src/function/sync/apply-actions/dispatch-parent-action/ignoremanipulation.ts +++ b/src/function/sync/apply-actions/dispatch-parent-action/ignoremanipulation.ts @@ -70,6 +70,16 @@ export async function dispatchIgnoreManipulation ({ action, cache }: { deviceEntry.hadManipulation = false } + if (action.ignoreHadManipulationFlags !== 0) { + const newFlags = deviceEntry.hadManipulationFlags & (~action.ignoreHadManipulationFlags) + + deviceEntry.hadManipulationFlags = newFlags + + if (newFlags === 0) { + deviceEntry.hadManipulation = false + } + } + await deviceEntry.save({ transaction: cache.transaction }) cache.invalidiateDeviceList = true } diff --git a/src/function/sync/get-server-data-status.ts b/src/function/sync/get-server-data-status.ts index 2a128e9..414e55b 100644 --- a/src/function/sync/get-server-data-status.ts +++ b/src/function/sync/get-server-data-status.ts @@ -89,6 +89,7 @@ export const generateServerDataStatus = async ({ database, clientStatus, familyI tDisablingAdmin: item.triedDisablingDeviceAdmin, reboot: item.didReboot, hadManipulation: item.hadManipulation, + hadManipulationFlags: item.hadManipulationFlags, reportUninstall: item.didDeviceReportUninstall, isUserKeptSignedIn: item.isUserKeptSignedIn, showDeviceConnected: item.showDeviceConnected, diff --git a/src/object/serverdatastatus.ts b/src/object/serverdatastatus.ts index b3580ab..7c8ab5f 100644 --- a/src/object/serverdatastatus.ts +++ b/src/object/serverdatastatus.ts @@ -77,6 +77,7 @@ export interface ServerDeviceData { tDisablingAdmin: boolean reboot: boolean hadManipulation: boolean + hadManipulationFlags: number reportUninstall: boolean isUserKeptSignedIn: boolean showDeviceConnected: boolean