diff --git a/src/function/sync/apply-actions/dispatch-parent-action/index.ts b/src/function/sync/apply-actions/dispatch-parent-action/index.ts index ce8da95..a22d39a 100644 --- a/src/function/sync/apply-actions/dispatch-parent-action/index.ts +++ b/src/function/sync/apply-actions/dispatch-parent-action/index.ts @@ -150,6 +150,8 @@ export const dispatchParentAction = async ({ return dispatchUpdateCategoryBlockedTimes({ action, cache, fromChildSelfLimitAddChildUserId }) } else if (action instanceof UpdateCategoryDisableLimitsAction) { return dispatchUpdateCategoryDisableLimits({ action, cache, fromChildSelfLimitAddChildUserId }) + } else if (action instanceof UpdateTimelimitRuleAction) { + return dispatchUpdateTimelimitRule({ action, cache, fromChildSelfLimitAddChildUserId }) } if (fromChildSelfLimitAddChildUserId !== null) { @@ -211,8 +213,6 @@ export const dispatchParentAction = async ({ return dispatchUpdateNetworkTimeVerification({ action, cache }) } else if (action instanceof UpdateParentNotificationFlagsAction) { return dispatchUpdateParentNotificationFlags({ action, cache }) - } else if (action instanceof UpdateTimelimitRuleAction) { - return dispatchUpdateTimelimitRule({ action, cache }) } else if (action instanceof RemoveUserAction) { return dispatchRemoveUser({ action, cache, parentUserId }) } else if (action instanceof ReportU2fLoginAction) { diff --git a/src/function/sync/apply-actions/dispatch-parent-action/updatetimelimitrule.ts b/src/function/sync/apply-actions/dispatch-parent-action/updatetimelimitrule.ts index dfe26e9..a39a026 100644 --- a/src/function/sync/apply-actions/dispatch-parent-action/updatetimelimitrule.ts +++ b/src/function/sync/apply-actions/dispatch-parent-action/updatetimelimitrule.ts @@ -17,11 +17,17 @@ import { UpdateTimelimitRuleAction } from '../../../../action' import { Cache } from '../cache' -import { MissingRuleException } from '../exception/missing-item' +import { MissingRuleException, MissingCategoryException } from '../exception/missing-item' +import { + CanNotModifyOtherUsersBySelfLimitationException, CanNotRelaxRestrictionsSelfLimitException +} from '../exception/self-limit' -export async function dispatchUpdateTimelimitRule ({ action, cache }: { +export async function dispatchUpdateTimelimitRule ({ + action, cache, fromChildSelfLimitAddChildUserId +}: { action: UpdateTimelimitRuleAction cache: Cache + fromChildSelfLimitAddChildUserId: string | null }) { const ruleEntry = await cache.database.timelimitRule.findOne({ where: { @@ -35,6 +41,53 @@ export async function dispatchUpdateTimelimitRule ({ action, cache }: { throw new MissingRuleException() } + if (fromChildSelfLimitAddChildUserId != null) { + const categoryEntryUnsafe = await cache.database.category.findOne({ + where: { + familyId: cache.familyId, + categoryId: ruleEntry.categoryId + }, + transaction: cache.transaction, + attributes: ['childId'] + }) + + if (!categoryEntryUnsafe) { + throw new MissingCategoryException() + } + + const categoryEntry = { + childId: categoryEntryUnsafe.childId + } + + + if (fromChildSelfLimitAddChildUserId !== categoryEntry.childId) { + throw new CanNotModifyOtherUsersBySelfLimitationException() + } + + const wasSessionDurationLimitationEnabled = + ruleEntry.sessionPauseMilliseconds > 0 && ruleEntry.sessionDurationMilliseconds > 0 + + const countOldAffectedDays = Array(7) + .fill(0) + .reduce((sum, _, index) => sum + ((ruleEntry.dayMaskAsBitmask >> index) & 1), 0) + + const isAtLeastAsStrictAsPreviously = + action.maximumTimeInMillis <= ruleEntry.maximumTimeInMillis && + (action.dayMask & ruleEntry.dayMaskAsBitmask) === ruleEntry.dayMaskAsBitmask && + (action.applyToExtraTimeUsage || !ruleEntry.applyToExtraTimeUsage) && + action.start <= ruleEntry.startMinuteOfDay && + action.end >= ruleEntry.endMinuteOfDay && + (!wasSessionDurationLimitationEnabled || ( + action.sessionDurationMilliseconds <= ruleEntry.sessionDurationMilliseconds && + action.sessionPauseMilliseconds >= ruleEntry.sessionPauseMilliseconds + )) && + (!action.perDay || ruleEntry.perDay || countOldAffectedDays <= 1) + + if (!isAtLeastAsStrictAsPreviously) { + throw new CanNotRelaxRestrictionsSelfLimitException() + } + } + ruleEntry.applyToExtraTimeUsage = action.applyToExtraTimeUsage ruleEntry.dayMaskAsBitmask = action.dayMask ruleEntry.maximumTimeInMillis = action.maximumTimeInMillis diff --git a/src/function/sync/apply-actions/exception/self-limit.ts b/src/function/sync/apply-actions/exception/self-limit.ts index f3b9241..809c527 100644 --- a/src/function/sync/apply-actions/exception/self-limit.ts +++ b/src/function/sync/apply-actions/exception/self-limit.ts @@ -1,6 +1,6 @@ /* * server component for the TimeLimit App - * Copyright (C) 2019 - 2020 Jonas Lochmann + * Copyright (C) 2019 - 2022 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 @@ -32,3 +32,9 @@ export class ActionNotSupportedBySelfLimitationException extends SelfLimitationE } export class SelfLimitNotPossibleException extends SelfLimitationException {} + +export class CanNotRelaxRestrictionsSelfLimitException extends SelfLimitationException { + constructor () { + super({ staticMessage: 'can not relax restrictions with the self limitation' }) + } +} diff --git a/src/function/sync/get-server-data-status/index.ts b/src/function/sync/get-server-data-status/index.ts index 5d44e32..39182b8 100644 --- a/src/function/sync/get-server-data-status/index.ts +++ b/src/function/sync/get-server-data-status/index.ts @@ -59,7 +59,7 @@ export const generateServerDataStatus = async ({ familyEntry.hasFullVersion ? parseInt(familyEntry.fullVersionUntil, 10) : 0 ), message: await getStatusMessage({ database, transaction }) || undefined, - apiLevel: 6 + apiLevel: 7 } if (familyEntry.deviceListVersion !== clientStatus.devices) {