Add support for child users adding limits themself

This commit is contained in:
Jonas Lochmann
2020-08-03 02:00:00 +02:00
parent a73af1338e
commit 007b1f2ec9
11 changed files with 375 additions and 99 deletions
+1
View File
@@ -44,6 +44,7 @@ The integrity field of a action may have got one of the following values:
- an empty string when no user authentication is required/ for app logic actions (e.g. incrementing the used time)
- the string ``device`` in case of parent actions if a parent is assigned to the device and asking for the password was disabled
- ``sha512(sequence number as string with the base 10 + the device id as string + the hash of the user password using the second salt as string + the encoded action as string)`` for parent and child actions
- the string ``childDevice`` in case the child wants to add limits for itself using parent actions; this feature must be enabled for the child and this allows only some actions with some parameters
In case of a invalid integrity value, the action is ignored and the client is told to do a full sync
@@ -18,11 +18,13 @@
import * as Sequelize from 'sequelize'
import { AddCategoryAppsAction } from '../../../../action'
import { CategoryAppAttributes } from '../../../../database/categoryapp'
import { getCategoryWithParentCategories } from '../../../../util/category'
import { Cache } from '../cache'
export async function dispatchAddCategoryApps ({ action, cache }: {
export async function dispatchAddCategoryApps ({ action, cache, fromChildSelfLimitAddChildUserId }: {
action: AddCategoryAppsAction
cache: Cache
fromChildSelfLimitAddChildUserId: string | null
}) {
const categoryEntryUnsafe = await cache.database.category.findOne({
where: {
@@ -39,14 +41,25 @@ export async function dispatchAddCategoryApps ({ action, cache }: {
const { childId } = categoryEntryUnsafe
if (fromChildSelfLimitAddChildUserId !== null) {
if (childId !== fromChildSelfLimitAddChildUserId) {
throw new Error('can not add apps to other users')
}
}
const categoriesOfSameChild = await cache.database.category.findAll({
where: {
familyId: cache.familyId,
childId
},
attributes: ['categoryId'],
attributes: ['categoryId', 'parentCategoryId'],
transaction: cache.transaction
}).map((item) => ({ categoryId: item.categoryId }))
}).map((item) => ({
categoryId: item.categoryId,
parentCategoryId: item.parentCategoryId
}))
const userCategoryIds = categoriesOfSameChild.map((item) => item.categoryId)
const oldCategories = await cache.database.categoryApp.findAll({
attributes: [ 'categoryId' ],
@@ -54,7 +67,7 @@ export async function dispatchAddCategoryApps ({ action, cache }: {
where: {
familyId: cache.familyId,
categoryId: {
[Sequelize.Op.in]: categoriesOfSameChild.map((item) => item.categoryId)
[Sequelize.Op.in]: userCategoryIds
},
packageName: {
[Sequelize.Op.in]: action.packageNames
@@ -63,6 +76,68 @@ export async function dispatchAddCategoryApps ({ action, cache }: {
transaction: cache.transaction
}).map((item) => item.categoryId)
if (fromChildSelfLimitAddChildUserId !== null) {
const parentCategoriesOfTargetCategory = getCategoryWithParentCategories(categoriesOfSameChild, action.categoryId)
const userEntryUnsafe = await cache.database.user.findOne({
attributes: [ 'categoryForNotAssignedApps' ],
where: {
familyId: cache.familyId,
userId: fromChildSelfLimitAddChildUserId
},
transaction: cache.transaction
})
if (!userEntryUnsafe) {
throw new Error('illegal state')
}
const userEntry = { categoryForNotAssignedApps: userEntryUnsafe.categoryForNotAssignedApps }
const validatedDefaultCategoryId = categoriesOfSameChild.find((item) => item.categoryId === userEntry.categoryForNotAssignedApps)?.categoryId
const allowUnassignedElements = validatedDefaultCategoryId !== undefined &&
parentCategoriesOfTargetCategory.indexOf(validatedDefaultCategoryId) !== -1
const assertCanAddApp = async (packageName: string, isApp: boolean) => {
const categoryAppEntryUnsafe = await cache.database.categoryApp.findOne({
attributes: [ 'categoryId' ],
where: {
familyId: cache.familyId,
categoryId: {
[Sequelize.Op.in]: userCategoryIds
},
packageName: packageName
},
transaction: cache.transaction
})
const categoryAppEntry = categoryAppEntryUnsafe ? { categoryId: categoryAppEntryUnsafe.categoryId } : null
if (categoryAppEntry === null) {
if ((isApp && allowUnassignedElements) || (!isApp)) {
// allow
} else {
throw new Error('can not assign apps without category as child')
}
} else {
if (parentCategoriesOfTargetCategory.indexOf(categoryAppEntry.categoryId) !== -1) {
// allow
} else {
throw new Error('can not add app which is not contained in the parent category')
}
}
}
for (let i = 0; i < action.packageNames.length; i++) {
const packageName = action.packageNames[i]
if (packageName.indexOf(':') !== -1) {
await assertCanAddApp(packageName.substring(0, packageName.indexOf(':')), true)
await assertCanAddApp(packageName, false)
} else {
await assertCanAddApp(packageName, true)
}
}
}
if (oldCategories.length > 0) {
await cache.database.categoryApp.destroy({
where: {
@@ -19,10 +19,17 @@ import { CreateCategoryAction } from '../../../../action'
import { generateVersionId } from '../../../../util/token'
import { Cache } from '../cache'
export async function dispatchCreateCategory ({ action, cache }: {
export async function dispatchCreateCategory ({ action, cache, fromChildSelfLimitAddChildUserId }: {
action: CreateCategoryAction
cache: Cache
fromChildSelfLimitAddChildUserId: string | null
}) {
if (fromChildSelfLimitAddChildUserId !== null) {
if (fromChildSelfLimitAddChildUserId !== action.childId) {
throw new Error('can not create categories for other child users')
}
}
// check that the child exists
const childEntry = await cache.database.user.findOne({
where: {
@@ -18,16 +18,30 @@
import { CreateTimeLimitRuleAction } from '../../../../action'
import { Cache } from '../cache'
export async function dispatchCreateTimeLimitRule ({ action, cache }: {
export async function dispatchCreateTimeLimitRule ({ action, cache, fromChildSelfLimitAddChildUserId }: {
action: CreateTimeLimitRuleAction
cache: Cache
fromChildSelfLimitAddChildUserId: string | null
}) {
const doesCategoryExist = await cache.doesCategoryExist(action.rule.categoryId)
const categoryEntryUnsafe = await cache.database.category.findOne({
where: {
familyId: cache.familyId,
categoryId: action.rule.categoryId
},
transaction: cache.transaction,
attributes: ['childId']
})
if (!doesCategoryExist) {
if (!categoryEntryUnsafe) {
throw new Error('invalid category id for new rule')
}
if (fromChildSelfLimitAddChildUserId !== null) {
if (fromChildSelfLimitAddChildUserId !== categoryEntryUnsafe.childId) {
throw new Error('can not add rules for other users')
}
}
await cache.database.timelimitRule.create({
familyId: cache.familyId,
ruleId: action.rule.ruleId,
@@ -102,94 +102,99 @@ import { dispatchUpdateTimelimitRule } from './updatetimelimitrule'
import { dispatchUpdateUserFlagsAction } from './updateuserflags'
import { dispatchUpdateUserLimitLoginCategoryAction } from './updateuserlimitlogincategory'
export const dispatchParentAction = async ({ action, cache, parentUserId, sourceDeviceId }: {
export const dispatchParentAction = async ({ action, cache, parentUserId, sourceDeviceId, fromChildSelfLimitAddChildUserId }: {
action: ParentAction
cache: Cache
parentUserId: string
sourceDeviceId: string | null
fromChildSelfLimitAddChildUserId: string | null
}) => {
if (action instanceof AddCategoryAppsAction) {
await dispatchAddCategoryApps({ action, cache })
} else if (action instanceof AddUserAction) {
await dispatchAddUser({ action, cache })
} else if (action instanceof RemoveCategoryAppsAction) {
await dispatchRemoveCategoryApps({ action, cache })
return dispatchAddCategoryApps({ action, cache, fromChildSelfLimitAddChildUserId })
} else if (action instanceof CreateCategoryAction) {
await dispatchCreateCategory({ action, cache })
return dispatchCreateCategory({ action, cache, fromChildSelfLimitAddChildUserId })
} else if (action instanceof CreateTimeLimitRuleAction) {
await dispatchCreateTimeLimitRule({ action, cache })
} else if (action instanceof DeleteCategoryAction) {
await dispatchDeleteCategory({ action, cache })
} else if (action instanceof UpdateCategoryTitleAction) {
await dispatchUpdateCategoryTitle({ action, cache })
} else if (action instanceof SetCategoryExtraTimeAction) {
await dispatchSetCategoryExtraTime({ action, cache })
} else if (action instanceof SetCategoryForUnassignedAppsAction) {
await dispatchSetCategoryForUnassignedApps({ action, cache })
} else if (action instanceof SetChildPasswordAction) {
await dispatchSetChildPassword({ action, cache })
} else if (action instanceof SetConsiderRebootManipulationAction) {
await dispatchSetConsiderRebootManipulation({ action, cache })
} else if (action instanceof SetDeviceDefaultUserAction) {
await dispatchSetDeviceDefaultUser({ action, cache })
} else if (action instanceof SetDeviceDefaultUserTimeoutAction) {
await dispatchSetDeviceDefaultUserTimeout({ action, cache })
} else if (action instanceof SetDeviceUserAction) {
await dispatchSetDeviceUser({ action, cache })
} else if (action instanceof SetKeepSignedInAction) {
await dispatchSetKeepSignedIn({ action, cache, parentUserId })
} else if (action instanceof SetParentCategoryAction) {
await dispatchSetParentCategory({ action, cache })
} else if (action instanceof SetRelaxPrimaryDeviceAction) {
await dispatchSetRelaxPrimaryDevice({ action, cache })
} else if (action instanceof SetSendDeviceConnected) {
await dispatchSetSendDeviceConnected({ action, cache, sourceDeviceId })
} else if (action instanceof SetUserDisableLimitsUntilAction) {
await dispatchUserSetDisableLimitsUntil({ action, cache })
} else if (action instanceof SetUserTimezoneAction) {
await dispatchSetUserTimezone({ action, cache })
} else if (action instanceof UpdateCategoryBatteryLimitAction) {
await dispatchUpdateCategoryBatteryLimit({ action, cache })
return dispatchCreateTimeLimitRule({ action, cache, fromChildSelfLimitAddChildUserId })
} else if (action instanceof UpdateCategoryBlockAllNotificationsAction) {
await dispatchUpdateCategoryBlockAllNotifications({ action, cache })
} else if (action instanceof UpdateCategoryBlockedTimesAction) {
await dispatchUpdateCategoryBlockedTimes({ action, cache })
} else if (action instanceof UpdateCategorySortingAction) {
await dispatchUpdateCategorySorting({ action, cache })
} else if (action instanceof IncrementCategoryExtraTimeAction) {
await dispatchIncrementCategoryExtraTime({ action, cache })
return dispatchUpdateCategoryBlockAllNotifications({ action, cache, fromChildSelfLimitAddChildUserId })
} else if (action instanceof SetParentCategoryAction) {
return dispatchSetParentCategory({ action, cache, fromChildSelfLimitAddChildUserId })
} else if (action instanceof UpdateCategoryTemporarilyBlockedAction) {
await dispatchUpdateCategoryTemporarilyBlocked({ action, cache })
} else if (action instanceof DeleteTimeLimitRuleAction) {
await dispatchDeleteTimeLimitRule({ action, cache })
} else if (action instanceof UpdateDeviceNameAction) {
await dispatchUpdateDeviceName({ action, cache })
} else if (action instanceof UpdateEnableActivityLevelBlockingAction) {
await dispatchUpdateEnableActivityLevelBlocking({ action, cache })
} else if (action instanceof UpdateNetworkTimeVerificationAction) {
await dispatchUpdateNetworkTimeVerification({ action, cache })
} else if (action instanceof UpdateParentNotificationFlagsAction) {
await dispatchUpdateParentNotificationFlags({ action, cache })
} else if (action instanceof UpdateTimelimitRuleAction) {
await dispatchUpdateTimelimitRule({ action, cache })
} else if (action instanceof RemoveUserAction) {
await dispatchRemoveUser({ action, cache, parentUserId })
} else if (action instanceof RenameChildAction) {
await dispatchRenameChild({ action, cache })
} else if (action instanceof ChangeParentPasswordAction) {
await dispatchChangeParentPassword({ action, cache })
} else if (action instanceof IgnoreManipulationAction) {
await dispatchIgnoreManipulation({ action, cache })
} else if (action instanceof UpdateCategoryTimeWarningsAction) {
await dispatchUpdateCategoryTimeWarnings({ action, cache })
} else if (action instanceof ResetParentBlockedTimesAction) {
await dispatchResetParentBlockedTimes({ action, cache })
} else if (action instanceof UpdateParentBlockedTimesAction) {
await dispatchUpdateParentBlockedTimes({ action, cache, parentUserId })
} else if (action instanceof UpdateUserFlagsAction) {
await dispatchUpdateUserFlagsAction({ action, cache })
} else if (action instanceof UpdateUserLimitLoginCategory) {
await dispatchUpdateUserLimitLoginCategoryAction({ action, cache, parentUserId })
return dispatchUpdateCategoryTemporarilyBlocked({ action, cache, fromChildSelfLimitAddChildUserId })
}
if (fromChildSelfLimitAddChildUserId === null) {
if (action instanceof AddUserAction) {
return dispatchAddUser({ action, cache })
} else if (action instanceof RemoveCategoryAppsAction) {
return dispatchRemoveCategoryApps({ action, cache })
} else if (action instanceof DeleteCategoryAction) {
return dispatchDeleteCategory({ action, cache })
} else if (action instanceof UpdateCategoryTitleAction) {
return dispatchUpdateCategoryTitle({ action, cache })
} else if (action instanceof SetCategoryExtraTimeAction) {
return dispatchSetCategoryExtraTime({ action, cache })
} else if (action instanceof SetCategoryForUnassignedAppsAction) {
return dispatchSetCategoryForUnassignedApps({ action, cache })
} else if (action instanceof SetChildPasswordAction) {
return dispatchSetChildPassword({ action, cache })
} else if (action instanceof SetConsiderRebootManipulationAction) {
return dispatchSetConsiderRebootManipulation({ action, cache })
} else if (action instanceof SetDeviceDefaultUserAction) {
return dispatchSetDeviceDefaultUser({ action, cache })
} else if (action instanceof SetDeviceDefaultUserTimeoutAction) {
return dispatchSetDeviceDefaultUserTimeout({ action, cache })
} else if (action instanceof SetDeviceUserAction) {
return dispatchSetDeviceUser({ action, cache })
} else if (action instanceof SetKeepSignedInAction) {
return dispatchSetKeepSignedIn({ action, cache, parentUserId })
} else if (action instanceof SetRelaxPrimaryDeviceAction) {
return dispatchSetRelaxPrimaryDevice({ action, cache })
} else if (action instanceof SetSendDeviceConnected) {
return dispatchSetSendDeviceConnected({ action, cache, sourceDeviceId })
} else if (action instanceof SetUserDisableLimitsUntilAction) {
return dispatchUserSetDisableLimitsUntil({ action, cache })
} else if (action instanceof SetUserTimezoneAction) {
return dispatchSetUserTimezone({ action, cache })
} else if (action instanceof UpdateCategoryBatteryLimitAction) {
return dispatchUpdateCategoryBatteryLimit({ action, cache })
} else if (action instanceof UpdateCategoryBlockedTimesAction) {
return dispatchUpdateCategoryBlockedTimes({ action, cache })
} else if (action instanceof UpdateCategorySortingAction) {
return dispatchUpdateCategorySorting({ action, cache })
} else if (action instanceof IncrementCategoryExtraTimeAction) {
return dispatchIncrementCategoryExtraTime({ action, cache })
} else if (action instanceof DeleteTimeLimitRuleAction) {
return dispatchDeleteTimeLimitRule({ action, cache })
} else if (action instanceof UpdateDeviceNameAction) {
return dispatchUpdateDeviceName({ action, cache })
} else if (action instanceof UpdateEnableActivityLevelBlockingAction) {
return dispatchUpdateEnableActivityLevelBlocking({ action, cache })
} else if (action instanceof UpdateNetworkTimeVerificationAction) {
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 RenameChildAction) {
return dispatchRenameChild({ action, cache })
} else if (action instanceof ChangeParentPasswordAction) {
return dispatchChangeParentPassword({ action, cache })
} else if (action instanceof IgnoreManipulationAction) {
return dispatchIgnoreManipulation({ action, cache })
} else if (action instanceof UpdateCategoryTimeWarningsAction) {
return dispatchUpdateCategoryTimeWarnings({ action, cache })
} else if (action instanceof ResetParentBlockedTimesAction) {
return dispatchResetParentBlockedTimes({ action, cache })
} else if (action instanceof UpdateParentBlockedTimesAction) {
return dispatchUpdateParentBlockedTimes({ action, cache, parentUserId })
} else if (action instanceof UpdateUserFlagsAction) {
return dispatchUpdateUserFlagsAction({ action, cache })
} else if (action instanceof UpdateUserLimitLoginCategory) {
return dispatchUpdateUserLimitLoginCategoryAction({ action, cache, parentUserId })
}
} else {
throw new Error('unsupported action type')
}
@@ -16,11 +16,13 @@
*/
import { SetParentCategoryAction } from '../../../../action'
import { getCategoryWithParentCategories } from '../../../../util/category'
import { Cache } from '../cache'
export async function dispatchSetParentCategory ({ action, cache }: {
export async function dispatchSetParentCategory ({ action, cache, fromChildSelfLimitAddChildUserId }: {
action: SetParentCategoryAction
cache: Cache
fromChildSelfLimitAddChildUserId: string | null
}) {
const categoryEntry = await cache.database.category.findOne({
where: {
@@ -34,6 +36,12 @@ export async function dispatchSetParentCategory ({ action, cache }: {
throw new Error('tried to set parent category of non existent category')
}
if (fromChildSelfLimitAddChildUserId !== null) {
if (categoryEntry.childId !== fromChildSelfLimitAddChildUserId) {
throw new Error('can not set parent category for other user')
}
}
if (action.parentCategory !== '') {
const categoriesByUserId = (await cache.database.category.findAll({
where: {
@@ -74,6 +82,16 @@ export async function dispatchSetParentCategory ({ action, cache }: {
if (childCategoryIds.has(action.parentCategory) || action.parentCategory === action.categoryId) {
throw new Error('can not set a category as parent which is a child of the category')
}
if (fromChildSelfLimitAddChildUserId !== null) {
const ownParentCategory = categoriesByUserId.find((item) => item.categoryId === categoryEntry.parentCategoryId)
const enableDueToLimitAddingWhenChild = ownParentCategory === undefined ||
getCategoryWithParentCategories(categoriesByUserId, action.parentCategory).indexOf(ownParentCategory.categoryId) !== -1
if (!enableDueToLimitAddingWhenChild) {
throw new Error('can not change parent categories in a way which reduces limits')
}
}
}
await cache.database.category.update({
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 Jonas Lochmann
* Copyright (C) 2019 - 2020 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
@@ -18,10 +18,34 @@
import { UpdateCategoryBlockAllNotificationsAction } from '../../../../action'
import { Cache } from '../cache'
export async function dispatchUpdateCategoryBlockAllNotifications ({ action, cache }: {
export async function dispatchUpdateCategoryBlockAllNotifications ({ action, cache, fromChildSelfLimitAddChildUserId }: {
action: UpdateCategoryBlockAllNotificationsAction
cache: Cache
fromChildSelfLimitAddChildUserId: string | null
}) {
const categoryEntryUnsafe = await cache.database.category.findOne({
where: {
familyId: cache.familyId,
categoryId: action.categoryId
},
transaction: cache.transaction,
attributes: ['childId']
})
if (!categoryEntryUnsafe) {
throw new Error('invalid category id for updating notification blocking')
}
if (fromChildSelfLimitAddChildUserId !== null) {
if (fromChildSelfLimitAddChildUserId !== categoryEntryUnsafe.childId) {
throw new Error('can not add rules for other users')
}
if (!action.blocked) {
throw new Error('can not disable filter as child')
}
}
const [affectedRows] = await cache.database.category.update({
blockAllNotifications: action.blocked
}, {
@@ -18,9 +18,10 @@
import { UpdateCategoryTemporarilyBlockedAction } from '../../../../action'
import { Cache } from '../cache'
export async function dispatchUpdateCategoryTemporarilyBlocked ({ action, cache }: {
export async function dispatchUpdateCategoryTemporarilyBlocked ({ action, cache, fromChildSelfLimitAddChildUserId }: {
action: UpdateCategoryTemporarilyBlockedAction
cache: Cache
fromChildSelfLimitAddChildUserId: string | null
}) {
if (action.blocked === true) {
if (!cache.hasFullVersion) {
@@ -28,6 +29,41 @@ export async function dispatchUpdateCategoryTemporarilyBlocked ({ action, cache
}
}
const categoryEntryUnsafe = await cache.database.category.findOne({
where: {
familyId: cache.familyId,
categoryId: action.categoryId
},
transaction: cache.transaction,
attributes: ['childId', 'temporarilyBlocked', 'temporarilyBlockedEndTime']
})
if (!categoryEntryUnsafe) {
throw new Error('invalid category id for updating temporarily blocking')
}
const categoryEntry = {
childId: categoryEntryUnsafe.childId,
temporarilyBlocked: categoryEntryUnsafe.temporarilyBlocked,
temporarilyBlockedEndTime: categoryEntryUnsafe.temporarilyBlockedEndTime
}
if (fromChildSelfLimitAddChildUserId !== null) {
if (fromChildSelfLimitAddChildUserId !== categoryEntry.childId) {
throw new Error('can not update temporarily blocking as child for other users')
}
if (action.endTime === undefined || !action.blocked) {
throw new Error('the child may only enable a temporarily blocking')
}
if (categoryEntry.temporarilyBlocked) {
if (action.endTime < categoryEntry.temporarilyBlockedEndTime || categoryEntry.temporarilyBlockedEndTime === 0) {
throw new Error('the child may not reduce the temporarily blocking')
}
}
}
const [affectedRows] = await cache.database.category.update({
temporarilyBlocked: action.blocked,
temporarilyBlockedEndTime: action.blocked ? (action.endTime ?? 0) : 0
+63 -8
View File
@@ -22,6 +22,7 @@ import { ClientPushChangesRequest } from '../../../api/schema'
import { isSerializedAppLogicAction, isSerializedChildAction, isSerializedParentAction } from '../../../api/validator'
import { VisibleConnectedDevicesManager } from '../../../connected-devices'
import { Database } from '../../../database'
import { UserFlags } from '../../../model/userflags'
import { EventHandler } from '../../../monitoring/eventhandler'
import { WebsocketApi } from '../../../websocket'
import { notifyClientsAboutChanges } from '../../websocket'
@@ -106,6 +107,8 @@ export const applyActionsFromDevice = async ({ database, request, websocket, con
// update the next sequence number
nextSequenceNumber = action.sequenceNumber + 1
let isChildLimitAdding = false
if (action.type === 'parent') {
if (action.integrity === 'device') {
const deviceEntryUnsafe2 = await cache.database.device.findOne({
@@ -125,6 +128,9 @@ export const applyActionsFromDevice = async ({ database, request, websocket, con
// this ensures that the parent exists
await cache.getSecondPasswordHashOfParent(action.userId)
} else if (action.integrity === 'childDevice') {
// will be checked later
isChildLimitAdding = true
} else {
const parentSecondHash = await cache.getSecondPasswordHashOfParent(action.userId)
@@ -191,19 +197,68 @@ export const applyActionsFromDevice = async ({ database, request, websocket, con
throw new Error('invalid action' + action.encodedAction)
}
eventHandler.countEvent('applyActionsFromDevice action:' + parsedSerializedAction.type)
eventHandler.countEvent('applyActionsFromDevice, childAddLimit: ' + isChildLimitAdding + ' action:' + parsedSerializedAction.type)
const parsedAction = parseParentAction(parsedSerializedAction)
try {
await dispatchParentAction({
action: parsedAction,
cache,
parentUserId: action.userId,
sourceDeviceId: deviceEntry.deviceId
})
if (isChildLimitAdding) {
const deviceEntryUnsafe2 = await cache.database.device.findOne({
attributes: ['currentUserId'],
where: {
familyId: cache.familyId,
deviceId: deviceEntry.deviceId,
currentUserId: action.userId
},
transaction: cache.transaction
})
if (!deviceEntryUnsafe2) {
throw new Error('illegal state')
}
const deviceUserId = deviceEntryUnsafe2.currentUserId
if (!deviceUserId) {
throw new Error('no device user id set but child add self limit action requested')
}
const deviceUserEntryUnsafe = await cache.database.user.findOne({
attributes: ['flags'],
where: {
familyId: cache.familyId,
userId: deviceUserId,
type: 'child'
},
transaction: cache.transaction
})
if (!deviceUserEntryUnsafe) {
throw new Error('no child user found for child limit adding action')
}
if ((parseInt(deviceUserEntryUnsafe.flags, 10) & UserFlags.ALLOW_SELF_LIMIT_ADD) !== UserFlags.ALLOW_SELF_LIMIT_ADD) {
throw new Error('child add limit action found but not allowed')
}
await dispatchParentAction({
action: parsedAction,
cache,
parentUserId: action.userId,
sourceDeviceId: deviceEntry.deviceId,
fromChildSelfLimitAddChildUserId: deviceUserId
})
} else {
await dispatchParentAction({
action: parsedAction,
cache,
parentUserId: action.userId,
sourceDeviceId: deviceEntry.deviceId,
fromChildSelfLimitAddChildUserId: null
})
}
} catch (ex) {
eventHandler.countEvent('applyActionsFromDevice actionWithError:' + parsedSerializedAction.type)
eventHandler.countEvent('applyActionsFromDeviceWithError, childAddLimit: ' + isChildLimitAdding + ' action:' + parsedSerializedAction.type)
throw ex
}
+2 -1
View File
@@ -17,5 +17,6 @@
export const UserFlags = {
RESTRICT_VIEWING_TO_PARENTS: 1,
ALL_FLAGS: 1
ALLOW_SELF_LIMIT_ADD: 2,
ALL_FLAGS: 1 | 2
}
+40
View File
@@ -0,0 +1,40 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2020 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 <https://www.gnu.org/licenses/>.
*/
export function getCategoryWithParentCategories (categories: Array<{ categoryId: string, parentCategoryId: string }>, startCategoryId: string): Array<string> {
const categoryById = new Map<string, { categoryId: string, parentCategoryId: string }>()
categories.forEach((category) => categoryById.set(category.categoryId, category))
const startCategory = categoryById.get(startCategoryId)
if (!startCategory) {
throw new Error('start category not found')
}
const categoryIds = [ startCategoryId ]
let currentCategory = categoryById.get(startCategory.parentCategoryId)
while (currentCategory !== undefined && categoryIds.indexOf(currentCategory.categoryId) === -1) {
categoryIds.push(currentCategory.categoryId)
currentCategory = categoryById.get(currentCategory.parentCategoryId)
}
return categoryIds
}