/* * server component for the TimeLimit App * Copyright (C) 2019 - 2023 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 * as Sequelize from 'sequelize' import { NewPermissionStatus, newPermissionStatusValues } from '../model/newpermissionstatus' import { ProtectionLevel, protetionLevels } from '../model/protectionlevel' import { RuntimePermissionStatus, runtimePermissionStatusValues } from '../model/runtimepermissionstatus' 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 const DeviceManipulationFlags = { USED_FGS_KILLER: 1, ALL: 1 } export const minPlatformTypeLength = 1 export const maxPlatformTypeLength = 8 export const minPlatformLevel = 0 export const maxPlatformLevel = 128 export interface DeviceAttributesVersion1 { familyId: string deviceId: string name: string model: string addedAt: string currentUserId: string installedAppsVersion: string deviceAuthToken: string networkTime: 'disabled' | 'if possible' | 'enabled' nextSequenceNumber: number currentProtectionLevel: ProtectionLevel highestProtectionLevel: ProtectionLevel currentUsageStatsPermission: RuntimePermissionStatus highestUsageStatsPermission: RuntimePermissionStatus currentNotificationAccessPermission: NewPermissionStatus highestNotificationAccessPermission: NewPermissionStatus currentAppVersion: number highestAppVersion: number triedDisablingDeviceAdmin: boolean hadManipulation: boolean } export interface DeviceAttributesVersion2 { lastConnectivity: string notSeenForLongTime: boolean didDeviceReportUninstall: boolean } export interface DeviceAttributesVersion3 { isUserKeptSignedIn: boolean } export interface DeviceAttributesVersion4 { showDeviceConnected: boolean } export interface DeviceAttributesVersion5 { defaultUserId: string defaultUserTimeout: number } export interface DeviceAttributesVersion6 { didReboot: boolean considerRebootManipulation: boolean } export interface DeviceAttributesVersion7 { currentOverlayPermission: RuntimePermissionStatus highestOverlayPermission: RuntimePermissionStatus } export interface DeviceAttributesVersion8 { // as = accessibility service asEnabled: boolean wasAsEnabled: boolean } export interface DeviceAttributesVersion9 { activityLevelBlocking: boolean } export interface DeviceAttributesVersion10 { isQorLater: boolean } export interface DeviceAttributesVersion11 { hadManipulationFlags: number } export interface DeviceAttributesVersion12 { manipulationFlags: number } export interface DeviceAttributesVersion13 { publicKey: Buffer | null } export interface DeviceAttributesVersion14 { nextKeyReplySequenceNumber: string } export interface DeviceAttributesVersion15 { platformType: string | null platformLevel: number } export type DeviceAttributes = DeviceAttributesVersion1 & DeviceAttributesVersion2 & DeviceAttributesVersion3 & DeviceAttributesVersion4 & DeviceAttributesVersion5 & DeviceAttributesVersion6 & DeviceAttributesVersion7 & DeviceAttributesVersion8 & DeviceAttributesVersion9 & DeviceAttributesVersion10 & DeviceAttributesVersion11 & DeviceAttributesVersion12 & DeviceAttributesVersion13 & DeviceAttributesVersion14 & DeviceAttributesVersion15 export type DeviceModel = Sequelize.Model & DeviceAttributes export type DeviceModelStatic = typeof Sequelize.Model & { new (values?: object, options?: Sequelize.BuildOptions): DeviceModel; } export const attributesVersion1: SequelizeAttributes = { familyId: { ...familyIdColumn, primaryKey: true }, deviceId: { ...idWithinFamilyColumn, primaryKey: true }, name: { ...labelColumn }, model: { ...labelColumn }, addedAt: { ...timestampColumn }, currentUserId: { ...optionalIdWithinFamilyColumn }, installedAppsVersion: { ...versionColumn }, deviceAuthToken: { ...authTokenColumn }, networkTime: createEnumColumn(['disabled', 'if possible', 'enabled']), nextSequenceNumber: { type: Sequelize.INTEGER, allowNull: false }, currentProtectionLevel: createEnumColumn(protetionLevels), highestProtectionLevel: createEnumColumn(protetionLevels), currentUsageStatsPermission: createEnumColumn(runtimePermissionStatusValues), highestUsageStatsPermission: createEnumColumn(runtimePermissionStatusValues), currentNotificationAccessPermission: createEnumColumn(newPermissionStatusValues), highestNotificationAccessPermission: createEnumColumn(newPermissionStatusValues), currentAppVersion: { type: Sequelize.INTEGER, allowNull: false, validate: { min: 0 } }, highestAppVersion: { type: Sequelize.INTEGER, allowNull: false, validate: { min: 0 } }, triedDisablingDeviceAdmin: { ...booleanColumn }, hadManipulation: { ...booleanColumn } } export const attributesVersion2: SequelizeAttributes = { lastConnectivity: { ...timestampColumn, defaultValue: 0 }, notSeenForLongTime: { ...booleanColumn, defaultValue: false }, didDeviceReportUninstall: { ...booleanColumn, defaultValue: false } } export const attributesVersion3: SequelizeAttributes = { isUserKeptSignedIn: { ...booleanColumn, defaultValue: false } } export const attributesVersion4: SequelizeAttributes = { showDeviceConnected: { ...booleanColumn, defaultValue: false } } export const attributesVersion5: SequelizeAttributes = { defaultUserId: { ...optionalIdWithinFamilyColumn, defaultValue: '' }, defaultUserTimeout: { type: Sequelize.INTEGER, allowNull: false, defaultValue: 0, validate: { min: 0 } } } export const attributesVersion6: SequelizeAttributes = { didReboot: { ...booleanColumn, defaultValue: false }, considerRebootManipulation: { ...booleanColumn, defaultValue: false } } export const attributesVersion7: SequelizeAttributes = { currentOverlayPermission: { ...createEnumColumn(runtimePermissionStatusValues), defaultValue: 'not granted' }, highestOverlayPermission: { ...createEnumColumn(runtimePermissionStatusValues), defaultValue: 'not granted' } } export const attributesVersion8: SequelizeAttributes = { asEnabled: { ...booleanColumn, defaultValue: false }, wasAsEnabled: { ...booleanColumn, defaultValue: false } } export const attributesVersion9: SequelizeAttributes = { activityLevelBlocking: { ...booleanColumn, defaultValue: false } } export const attributesVersion10: SequelizeAttributes = { isQorLater: { ...booleanColumn, defaultValue: false } } export const attributesVersion11: SequelizeAttributes = { hadManipulationFlags: { type: Sequelize.INTEGER, allowNull: false, defaultValue: 0, validate: { min: 0, max: DeviceHadManipulationFlags.ALL } } } export const attributesVersion12: SequelizeAttributes = { manipulationFlags: { type: Sequelize.INTEGER, allowNull: false, defaultValue: 0, validate: { min: 0, max: DeviceManipulationFlags.ALL } } } export const attributesVersion13: SequelizeAttributes = { publicKey: { type: Sequelize.BLOB, allowNull: true, defaultValue: null } } export const attributesVersion14: SequelizeAttributes = { nextKeyReplySequenceNumber: { type: Sequelize.BIGINT, allowNull: false, defaultValue: 1 } } export const attributesVersion15: SequelizeAttributes = { platformType: { type: Sequelize.STRING(maxPlatformTypeLength), allowNull: true, defaultValue: null, validate: { len: [minPlatformTypeLength, maxPlatformTypeLength] } }, platformLevel: { type: Sequelize.INTEGER, allowNull: false, defaultValue: minPlatformLevel, validate: { min: minPlatformLevel, max: maxPlatformLevel } } } export const attributes: SequelizeAttributes = { ...attributesVersion1, ...attributesVersion2, ...attributesVersion3, ...attributesVersion4, ...attributesVersion5, ...attributesVersion6, ...attributesVersion7, ...attributesVersion8, ...attributesVersion9, ...attributesVersion10, ...attributesVersion11, ...attributesVersion12, ...attributesVersion13, ...attributesVersion14, ...attributesVersion15 } export const createDeviceModel = (sequelize: Sequelize.Sequelize): DeviceModelStatic => sequelize.define('Device', attributes) as DeviceModelStatic export const hasDeviceManipulation = (device: DeviceAttributes) => { const manipulationOfProtectionLevel = device.currentProtectionLevel !== device.highestProtectionLevel const manipulationOfUsageStats = device.currentUsageStatsPermission !== device.highestUsageStatsPermission const manipulationOfNotificationAccess = device.currentNotificationAccessPermission !== device.highestNotificationAccessPermission const manipulationOfAppVersion = device.currentAppVersion !== device.highestAppVersion const manipulationOfOverlayPermission = device.currentOverlayPermission !== device.highestOverlayPermission const manipulationOfAsPermission = device.asEnabled !== device.wasAsEnabled const hasActiveManipulationWarning = manipulationOfProtectionLevel || manipulationOfUsageStats || manipulationOfNotificationAccess || manipulationOfAppVersion || device.triedDisablingDeviceAdmin || device.didReboot || manipulationOfOverlayPermission || manipulationOfAsPermission const hasAnyManipulation = hasActiveManipulationWarning || device.hadManipulation || device.manipulationFlags !== 0 return hasAnyManipulation }