Add preblocking

This commit is contained in:
Jonas Lochmann
2020-12-21 01:00:00 +01:00
parent 13ede06a91
commit 2bb3777484
23 changed files with 615 additions and 71 deletions
+1
View File
@@ -74,3 +74,4 @@ export { MarkTaskPendingAction } from './marktaskpendingaction'
export { DeleteChildTaskAction } from './deletechildtaskaction'
export { UpdateChildTaskAction } from './updatechildtaskaction'
export { ReviewChildTaskAction } from './reviewchildtaskaction'
export { UpdateUserLimitLoginPreBlockDuration } from './updateuserlimitloginpreblockduration'
+5 -1
View File
@@ -62,6 +62,7 @@ import { SerializedUpdateParentNotificationFlagsAction, UpdateParentNotification
import { SerializedUpdateTimelimitRuleAction, UpdateTimelimitRuleAction } from '../updatetimelimitrule'
import { SerializedUpdateUserFlagsAction, UpdateUserFlagsAction } from '../updateuserflags'
import { SerializedUpdateUserLimitLoginCategory, UpdateUserLimitLoginCategory } from '../updateuserlimitlogincategory'
import { SerializedUpdateUserLimitLoginPreBlockDuration, UpdateUserLimitLoginPreBlockDuration } from '../updateuserlimitloginpreblockduration'
export type SerializedParentAction =
SerializedAddCategoryAppsAction |
@@ -108,7 +109,8 @@ export type SerializedParentAction =
SerializedUpdateParentNotificationFlagsAction |
SerializedUpdateTimelimitRuleAction |
SerializedUpdateUserFlagsAction |
SerializedUpdateUserLimitLoginCategory
SerializedUpdateUserLimitLoginCategory |
SerializedUpdateUserLimitLoginPreBlockDuration
export const parseParentAction = (action: SerializedParentAction): ParentAction => {
if (action.type === 'ADD_CATEGORY_APPS') {
@@ -201,6 +203,8 @@ export const parseParentAction = (action: SerializedParentAction): ParentAction
return UpdateUserFlagsAction.parse(action)
} else if (action.type === 'UPDATE_USER_LIMIT_LOGIN_CATEGORY') {
return UpdateUserLimitLoginCategory.parse(action)
} else if (action.type === 'UPDATE_USER_LIMIT_LOGIN_PRE_BLOCK_DURATION') {
return UpdateUserLimitLoginPreBlockDuration.parse(action)
} else {
throw new UnknownActionTypeException({ group: 'parent' })
}
@@ -0,0 +1,57 @@
/*
* 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/>.
*/
import { maxPreBlockDuration } from '../database/userlimitlogincategory'
import { ParentAction } from './basetypes'
import { assertIdWithinFamily, assertSafeInteger, throwOutOfRange } from './meta/util'
const actionType = 'UpdateUserLimitLoginPreBlockDuration'
export class UpdateUserLimitLoginPreBlockDuration extends ParentAction {
readonly userId: string
readonly preBlockDuration: number
constructor ({ userId, preBlockDuration }: {
userId: string,
preBlockDuration: number
}) {
super()
assertIdWithinFamily({ actionType, field: 'userId', value: userId })
assertSafeInteger({ actionType, field: 'preBlockDuration', value: preBlockDuration })
if (preBlockDuration < 0 || preBlockDuration > maxPreBlockDuration) {
throwOutOfRange({ actionType, field: 'preBlockDuration', value: preBlockDuration })
}
this.userId = userId
this.preBlockDuration = preBlockDuration
}
static parse = ({ userId, preBlockDuration }: SerializedUpdateUserLimitLoginPreBlockDuration) => (
new UpdateUserLimitLoginPreBlockDuration({
userId,
preBlockDuration
})
)
}
export interface SerializedUpdateUserLimitLoginPreBlockDuration {
type: 'UPDATE_USER_LIMIT_LOGIN_PRE_BLOCK_DURATION'
userId: string
preBlockDuration: number
}
+29
View File
@@ -1324,6 +1324,29 @@ const definitions = {
"userId"
]
},
"SerializedUpdateUserLimitLoginPreBlockDuration": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"UPDATE_USER_LIMIT_LOGIN_PRE_BLOCK_DURATION"
]
},
"userId": {
"type": "string"
},
"preBlockDuration": {
"type": "number"
}
},
"additionalProperties": false,
"required": [
"preBlockDuration",
"type",
"userId"
]
},
"SerializedAddInstalledAppsAction": {
"type": "object",
"properties": {
@@ -2370,6 +2393,9 @@ const definitions = {
},
"llc": {
"type": "string"
},
"pbd": {
"type": "number"
}
},
"additionalProperties": false,
@@ -2695,6 +2721,9 @@ export const isSerializedParentAction: (value: object) => value is SerializedPar
},
{
"$ref": "#/definitions/SerializedUpdateUserLimitLoginCategory"
},
{
"$ref": "#/definitions/SerializedUpdateUserLimitLoginPreBlockDuration"
}
],
"definitions": definitions,
@@ -0,0 +1,38 @@
/*
* 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/>.
*/
import { QueryInterface, Sequelize, Transaction } from 'sequelize'
import { attributesVersion2 as limitLoginCategoryAttributes } from '../../userlimitlogincategory'
export async function up (queryInterface: QueryInterface, sequelize: Sequelize) {
await sequelize.transaction({
type: Transaction.TYPES.EXCLUSIVE
}, async (transaction) => {
// timelimit rule table
await queryInterface.addColumn('UserLimitLoginCategories', 'preBlockDuration', {
...limitLoginCategoryAttributes.preBlockDuration
}, { transaction })
})
}
export async function down (queryInterface: QueryInterface, sequelize: Sequelize) {
await sequelize.transaction({
type: Transaction.TYPES.EXCLUSIVE
}, async (transaction) => {
await queryInterface.removeColumn('UserLimitLoginCategories', 'preBlockDuration', { transaction })
})
}
+27 -2
View File
@@ -19,21 +19,46 @@ import * as Sequelize from 'sequelize'
import { familyIdColumn, idWithinFamilyColumn } from './columns'
import { SequelizeAttributes } from './types'
export interface UserLimitLoginCategoryAttributes {
export const maxPreBlockDuration = 1000 * 60 * 60 * 24 // 1 day
export interface UserLimitLoginCategoryAttributesVersion1 {
familyId: string
userId: string
categoryId: string
}
export interface UserLimitLoginCategoryAttributesVersion2 {
preBlockDuration: number
}
export type UserLimitLoginCategoryAttributes = UserLimitLoginCategoryAttributesVersion1 & UserLimitLoginCategoryAttributesVersion2
export type UserLimitLoginCategoryModel = Sequelize.Model & UserLimitLoginCategoryAttributes
export type UserLimitLoginCategoryModelStatic = typeof Sequelize.Model & {
new (values?: object, options?: Sequelize.BuildOptions): UserLimitLoginCategoryModel;
}
export const attributes: SequelizeAttributes<UserLimitLoginCategoryAttributes> = {
export const attributesVersion1: SequelizeAttributes<UserLimitLoginCategoryAttributesVersion1> = {
familyId: { ...familyIdColumn, primaryKey: true },
userId: { ...idWithinFamilyColumn, primaryKey: true },
categoryId: { ...idWithinFamilyColumn }
}
export const attributesVersion2: SequelizeAttributes<UserLimitLoginCategoryAttributesVersion2> = {
preBlockDuration: {
type: Sequelize.INTEGER,
validate: {
min: 0,
max: maxPreBlockDuration
},
allowNull: false,
defaultValue: 0
}
}
export const attributes: SequelizeAttributes<UserLimitLoginCategoryAttributes> = {
...attributesVersion1,
...attributesVersion2
}
export const createUserLimitLoginCategoryModel = (sequelize: Sequelize.Sequelize): UserLimitLoginCategoryModelStatic => sequelize.define('UserLimitLoginCategory', attributes) as UserLimitLoginCategoryModelStatic
@@ -61,7 +61,8 @@ import {
UpdateParentNotificationFlagsAction,
UpdateTimelimitRuleAction,
UpdateUserFlagsAction,
UpdateUserLimitLoginCategory
UpdateUserLimitLoginCategory,
UpdateUserLimitLoginPreBlockDuration
} from '../../../../action'
import { Cache } from '../cache'
import { ActionObjectTypeNotHandledException } from '../exception/illegal-state'
@@ -111,6 +112,7 @@ import { dispatchUpdateParentNotificationFlags } from './updateparentnotificatio
import { dispatchUpdateTimelimitRule } from './updatetimelimitrule'
import { dispatchUpdateUserFlagsAction } from './updateuserflags'
import { dispatchUpdateUserLimitLoginCategoryAction } from './updateuserlimitlogincategory'
import { dispatchUpdateUserLimitPreBlockDuration } from './updateuserlimitloginpreblockduration'
export const dispatchParentAction = async ({ action, cache, parentUserId, sourceDeviceId, fromChildSelfLimitAddChildUserId }: {
action: ParentAction
@@ -214,6 +216,8 @@ export const dispatchParentAction = async ({ action, cache, parentUserId, source
await dispatchReviewChildTaskAction({ action, cache })
} else if (action instanceof UpdateChildTaskAction) {
await dispatchUpdateChildTaskAction({ action, cache })
} else if (action instanceof UpdateUserLimitLoginPreBlockDuration) {
await dispatchUpdateUserLimitPreBlockDuration({ action, cache, parentUserId })
} else {
throw new ActionObjectTypeNotHandledException()
}
@@ -68,7 +68,8 @@ export async function dispatchUpdateUserLimitLoginCategoryAction ({ action, cach
await cache.database.userLimitLoginCategory.create({
familyId: cache.familyId,
userId: action.userId,
categoryId: action.categoryId
categoryId: action.categoryId,
preBlockDuration: 0
}, {
transaction: cache.transaction
})
@@ -0,0 +1,72 @@
/*
* 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/>.
*/
import { UpdateUserLimitLoginPreBlockDuration } from '../../../../action'
import { Cache } from '../cache'
import { ApplyActionException } from '../exception/index'
import { MissingItemException, MissingUserException } from '../exception/missing-item'
export async function dispatchUpdateUserLimitPreBlockDuration ({ action, cache, parentUserId }: {
action: UpdateUserLimitLoginPreBlockDuration
cache: Cache
parentUserId: string
}) {
const userEntry = await cache.database.user.findOne({
where: {
familyId: cache.familyId,
userId: action.userId,
type: 'parent'
},
transaction: cache.transaction
})
if (!userEntry) {
throw new MissingUserException()
}
if (action.preBlockDuration !== 0 && parentUserId !== action.userId) {
throw new ApplyActionException({
staticMessage: 'only the parent user itself can add a limit login pre block duration'
})
}
const preBlockItem = await cache.database.userLimitLoginCategory.findOne({
transaction: cache.transaction,
where: {
familyId: cache.familyId,
userId: action.userId
}
})
if (preBlockItem === null) {
throw new MissingItemException({
staticMessage: 'you can not set a pre block duration if there is no pre block item'
})
}
await cache.database.userLimitLoginCategory.update({
preBlockDuration: action.preBlockDuration
}, {
transaction: cache.transaction,
where: {
familyId: cache.familyId,
userId: action.userId
}
})
cache.invalidiateUserList = true
}
@@ -67,19 +67,21 @@ export async function getUserList ({ database, transaction, familyEntry }: {
},
attributes: [
'userId',
'categoryId'
'categoryId',
'preBlockDuration'
],
transaction
})).map((item) => ({
userId: item.userId,
categoryId: item.categoryId
categoryId: item.categoryId,
preBlockDuration: item.preBlockDuration
}))
const getLimitLoginCategory = (userId: string) => {
const item = limitLoginCategories.find((item) => item.userId === userId)
if (item) {
return item.categoryId
return item
} else {
return undefined
}
@@ -87,22 +89,27 @@ export async function getUserList ({ database, transaction, familyEntry }: {
return {
version: familyEntry.userListVersion,
data: users.map((item) => ({
id: item.userId,
name: item.name,
password: item.passwordHash,
secondPasswordSalt: item.secondPasswordSalt,
type: item.type,
timeZone: item.timeZone,
disableLimitsUntil: parseInt(item.disableTimelimitsUntil, 10),
mail: item.mail,
currentDevice: item.currentDevice,
categoryForNotAssignedApps: item.categoryForNotAssignedApps,
relaxPrimaryDevice: item.relaxPrimaryDeviceRule,
mailNotificationFlags: item.mailNotificationFlags,
blockedTimes: '',
flags: parseInt(item.flags, 10),
llc: getLimitLoginCategory(item.userId)
}))
data: users.map((item) => {
const limitLoginCategory = getLimitLoginCategory(item.userId)
return {
id: item.userId,
name: item.name,
password: item.passwordHash,
secondPasswordSalt: item.secondPasswordSalt,
type: item.type,
timeZone: item.timeZone,
disableLimitsUntil: parseInt(item.disableTimelimitsUntil, 10),
mail: item.mail,
currentDevice: item.currentDevice,
categoryForNotAssignedApps: item.categoryForNotAssignedApps,
relaxPrimaryDevice: item.relaxPrimaryDeviceRule,
mailNotificationFlags: item.mailNotificationFlags,
blockedTimes: '',
flags: parseInt(item.flags, 10),
llc: limitLoginCategory?.categoryId,
pbd: limitLoginCategory?.preBlockDuration
}
})
}
}
+1
View File
@@ -61,6 +61,7 @@ export interface ServerUserEntry {
blockedTimes: string
flags: number
llc?: string // limit login category
pbd?: number // pre block duration, default is zero
}
export interface ServerDeviceData {