fix: assetById permissions + set min pg requirement to 16

vega
NGPixel 2 months ago
parent c3f562b315
commit 7a3d78bbac
No known key found for this signature in database
GPG Key ID: B755FB6870B30F63

@ -117,8 +117,8 @@ The server **dev** should already be available under **Servers**. If that's not
### Requirements
- PostgreSQL **12** or later *(**16** or later recommended)*
- Node.js **20.x** or later
- PostgreSQL **16** or later
- Node.js **24.x** or later
- [pnpm](https://pnpm.io/installation#using-corepack)
### Usage

@ -36,7 +36,7 @@ export default {
throw new Error('ERR_FORBIDDEN')
}
for (let str of args.providers) {
for (const str of args.providers) {
await WIKI.db.analytics.query().patch({
isEnabled: str.isEnabled,
config: reduce(str.config, (result, value, key) => {

@ -1,7 +1,7 @@
import _ from 'lodash-es'
import sanitize from 'sanitize-filename'
import { generateError, generateSuccess } from '../../helpers/graph.mjs'
import { decodeFolderPath, decodeTreePath, generateHash } from '../../helpers/common.mjs'
import { decodeTreePath, generateHash } from '../../helpers/common.mjs'
import path from 'node:path'
import fs from 'fs-extra'
import { v4 as uuid } from 'uuid'
@ -10,9 +10,12 @@ import { pipeline } from 'node:stream/promises'
export default {
Query: {
async assetById (obj, args, context) {
// FIXME: Perm
const asset = await WIKI.db.assets.query().findById(args.id)
const asset = await WIKI.db.assets.query().findById(args.id).withGraphFetched('tree')
if (asset) {
const assetPath = asset.tree.folderPath ? `${decodeTreePath(asset.tree.folderPath)}/${asset.tree.fileName}` : asset.tree.fileName
if (!WIKI.auth.checkAccess(context.req.user, ['read:assets'], { path: assetPath })) {
throw new Error('ERR_FORBIDDEN')
}
return asset
} else {
throw new Error('ERR_ASSET_NOT_FOUND')
@ -50,13 +53,13 @@ export default {
}
// Check source asset permissions
const assetSourcePath = (treeItem.folderPath) ? decodeTreePath(decodeFolderPath(treeItem.folderPath)) + `/${treeItem.fileName}` : treeItem.fileName
const assetSourcePath = (treeItem.folderPath) ? decodeTreePath(treeItem.folderPath) + `/${treeItem.fileName}` : treeItem.fileName
if (!WIKI.auth.checkAccess(context.req.user, ['manage:assets'], { path: assetSourcePath })) {
throw new Error('ERR_FORBIDDEN')
}
// Check target asset permissions
const assetTargetPath = (treeItem.folderPath) ? decodeTreePath(decodeFolderPath(treeItem.folderPath)) + `/${filename}` : filename
const assetTargetPath = (treeItem.folderPath) ? decodeTreePath(treeItem.folderPath) + `/${filename}` : filename
if (!WIKI.auth.checkAccess(context.req.user, ['write:assets'], { path: assetTargetPath })) {
throw new Error('ERR_TARGET_FORBIDDEN')
}
@ -107,7 +110,7 @@ export default {
const treeItem = await WIKI.db.tree.query().findById(args.id)
if (treeItem) {
// Check permissions
const assetPath = (treeItem.folderPath) ? decodeTreePath(decodeFolderPath(treeItem.folderPath)) + `/${treeItem.fileName}` : treeItem.fileName
const assetPath = (treeItem.folderPath) ? decodeTreePath(treeItem.folderPath) + `/${treeItem.fileName}` : treeItem.fileName
if (!WIKI.auth.checkAccess(context.req.user, ['manage:assets'], { path: assetPath })) {
throw new Error('ERR_FORBIDDEN')
}
@ -354,7 +357,7 @@ export default {
const failedResults = results.filter(r => r.status === 'rejected')
if (failedResults.length > 0) {
// -> One or more thrown errors
WIKI.logger.warn(`Failed to upload one or more assets:`)
WIKI.logger.warn('Failed to upload one or more assets:')
for (const failedResult of failedResults) {
WIKI.logger.warn(failedResult.reason)
}

@ -1,7 +1,5 @@
import _ from 'lodash-es'
import {
decodeFolderPath,
encodeFolderPath,
decodeTreePath,
encodeTreePath
} from '../../helpers/common.mjs'
@ -50,12 +48,12 @@ export default {
if (args.parentId) {
const parent = await WIKI.db.knex('tree').where('id', args.parentId).first()
if (parent) {
parentPath = (parent.folderPath ? `${decodeFolderPath(parent.folderPath)}.${parent.fileName}` : parent.fileName)
parentPath = (parent.folderPath ? `${parent.folderPath}.${parent.fileName}` : parent.fileName)
}
} else if (args.parentPath) {
parentPath = encodeTreePath(args.parentPath)
}
const folderPathCondition = parentPath ? `${encodeFolderPath(parentPath)}.${depthCondition}` : depthCondition
const folderPathCondition = parentPath ? `${parentPath}.${depthCondition}` : depthCondition
// Fetch Items
const items = await WIKI.db.knex('tree')
@ -67,7 +65,7 @@ export default {
const parentPathParts = parentPath.split('.')
for (let i = 0; i <= parentPathParts.length; i++) {
builder.orWhere({
folderPath: encodeFolderPath(_.dropRight(parentPathParts, i).join('.')),
folderPath: _.dropRight(parentPathParts, i).join('.'),
fileName: _.nth(parentPathParts, i * -1),
type: 'folder'
})
@ -103,7 +101,7 @@ export default {
id: item.id,
depth: item.depth,
type: item.type,
folderPath: decodeTreePath(decodeFolderPath(item.folderPath)),
folderPath: decodeTreePath(item.folderPath),
fileName: item.fileName,
title: item.title,
tags: item.tags ?? [],

@ -50,26 +50,6 @@ export function encodeTreePath (str) {
return str?.toLowerCase()?.replaceAll('/', '.') || ''
}
/**
* Encode a folder path (to support legacy PostgresSQL ltree)
*
* @param {string} val String to encode
* @returns Encoded folder path
*/
export function encodeFolderPath (val) {
return WIKI.db.LEGACY ? val?.replaceAll('-', '_') : val
}
/**
* Decode a folder path (to support legacy PostgresSQL ltree)
*
* @param {string} val String to decode
* @returns Decoded folder path
*/
export function decodeFolderPath (val) {
return WIKI.db.LEGACY ? val?.replaceAll('_', '-') : val
}
/**
* Generate SHA-1 Hash of a string
*

@ -4,6 +4,7 @@ import fse from 'fs-extra'
import { startsWith } from 'lodash-es'
import { generateHash } from '../helpers/common.mjs'
import { Tree } from './tree.mjs'
import { User } from './users.mjs'
/**
@ -40,6 +41,14 @@ export class Asset extends Model {
from: 'assets.authorId',
to: 'users.id'
}
},
tree: {
relation: Model.HasOneRelation,
modelClass: Tree,
join: {
from: 'assets.id',
to: 'tree.id'
}
}
}
}
@ -49,6 +58,7 @@ export class Asset extends Model {
this.updatedAt = new Date().toISOString()
}
async $beforeInsert (context) {
await super.$beforeInsert(context)
@ -78,7 +88,7 @@ export class Asset extends Model {
}).first()
// Build Object
let assetRow = {
const assetRow = {
filename: opts.originalname,
ext: fileInfo.ext,
kind: startsWith(opts.mimetype, 'image/') ? 'image' : 'binary',
@ -158,7 +168,9 @@ export class Asset extends Model {
return WIKI.db.tree.query()
.select('tree.*', 'assets.preview', 'assets.previewState')
.innerJoin('assets', 'tree.id', 'assets.id')
.where(id ? { 'tree.id': id } : {
.where(id
? { 'tree.id': id }
: {
'tree.hash': generateHash(path),
'tree.locale': locale,
'tree.siteId': siteId
@ -185,7 +197,7 @@ export class Asset extends Model {
// }
await WIKI.db.assets.getAssetFromDb({ pathArgs, fileHash, cachePath, siteId }, res)
} catch (err) {
if (err.code === `ECONNABORTED` || err.code === `EPIPE`) {
if (err.code === 'ECONNABORTED' || err.code === 'EPIPE') {
return
}
WIKI.logger.error(err)
@ -210,7 +222,7 @@ export class Asset extends Model {
path: assetPath
}
})
for (let location of localLocations.filter(location => Boolean(location.path))) {
for (const location of localLocations.filter(location => Boolean(location.path))) {
const assetExists = await WIKI.db.assets.getAssetFromCache(assetPath, location.path, res)
if (assetExists) {
return true
@ -235,6 +247,6 @@ export class Asset extends Model {
}
static async flushTempUploads () {
return fse.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `uploads`))
return fse.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads'))
}
}

@ -1,9 +1,7 @@
import { Model } from 'objection'
import { differenceWith, dropRight, last, nth } from 'lodash-es'
import {
decodeFolderPath,
decodeTreePath,
encodeFolderPath,
encodeTreePath,
generateHash
} from '../helpers/common.mjs'
@ -85,7 +83,7 @@ export class Tree extends Model {
const parentPath = encodeTreePath(path)
const parentPathParts = parentPath.split('.')
const parentFilter = {
folderPath: encodeFolderPath(dropRight(parentPathParts).join('.')),
folderPath: dropRight(parentPathParts).join('.'),
fileName: last(parentPathParts)
}
const parent = await WIKI.db.knex('tree').where({
@ -143,7 +141,7 @@ export class Tree extends Model {
const pageEntry = await WIKI.db.knex('tree').insert({
id,
folderPath: encodeFolderPath(folderPath),
folderPath,
fileName,
type: 'page',
title,
@ -191,7 +189,7 @@ export class Tree extends Model {
const assetEntry = await WIKI.db.knex('tree').insert({
id,
folderPath: encodeFolderPath(folderPath),
folderPath,
fileName,
type: 'asset',
title,
@ -231,7 +229,7 @@ export class Tree extends Model {
WIKI.logger.debug(`Creating new folder ${pathName}...`)
const parentPathParts = parentPath.split('.')
const parentFilter = {
folderPath: encodeFolderPath(dropRight(parentPathParts).join('.')),
folderPath: dropRight(parentPathParts).join('.'),
fileName: last(parentPathParts)
}
@ -242,7 +240,7 @@ export class Tree extends Model {
if (!parent) {
throw new Error('ERR_FOLDER_PARENT_INVALID')
}
parentPath = parent.folderPath ? `${decodeFolderPath(parent.folderPath)}.${parent.fileName}` : parent.fileName
parentPath = parent.folderPath ? `${parent.folderPath}.${parent.fileName}` : parent.fileName
} else if (parentPath) {
parent = await WIKI.db.knex('tree').where(parentFilter).first()
} else {
@ -253,7 +251,7 @@ export class Tree extends Model {
const existingFolder = await WIKI.db.knex('tree').select('id').where({
siteId,
locale,
folderPath: encodeFolderPath(parentPath),
folderPath: parentPath,
fileName: pathName,
type: 'folder'
}).first()
@ -268,7 +266,7 @@ export class Tree extends Model {
const parentPathParts = parentPath.split('.')
for (let i = 1; i <= parentPathParts.length; i++) {
const ancestor = {
folderPath: encodeFolderPath(dropRight(parentPathParts, i).join('.')),
folderPath: dropRight(parentPathParts, i).join('.'),
fileName: nth(parentPathParts, i * -1)
}
expectedAncestors.push(ancestor)
@ -303,7 +301,7 @@ export class Tree extends Model {
// Create folder
const fullPath = parentPath ? `${decodeTreePath(parentPath)}/${pathName}` : pathName
const folder = await WIKI.db.knex('tree').insert({
folderPath: encodeFolderPath(parentPath),
folderPath: parentPath,
fileName: pathName,
type: 'folder',
title,
@ -372,8 +370,8 @@ export class Tree extends Model {
}
// Build new paths
const oldFolderPath = encodeFolderPath(folder.folderPath ? `${folder.folderPath}.${folder.fileName}` : folder.fileName)
const newFolderPath = encodeFolderPath(folder.folderPath ? `${folder.folderPath}.${pathName}` : pathName)
const oldFolderPath = folder.folderPath ? `${folder.folderPath}.${folder.fileName}` : folder.fileName
const newFolderPath = folder.folderPath ? `${folder.folderPath}.${pathName}` : pathName
// Update children nodes
WIKI.logger.debug(`Updating parent path of children nodes from ${oldFolderPath} to ${newFolderPath} ...`)
@ -385,7 +383,7 @@ export class Tree extends Model {
})
// Rename the folder itself
const fullPath = folder.folderPath ? `${decodeFolderPath(folder.folderPath)}/${pathName}` : pathName
const fullPath = folder.folderPath ? `${folder.folderPath}/${pathName}` : pathName
await WIKI.db.knex('tree').where('id', folder.id).update({
fileName: pathName,
title,
@ -416,7 +414,7 @@ export class Tree extends Model {
WIKI.logger.debug(`Deleting folder ${folder.id} at path ${folderPath}...`)
// Delete all children
const deletedNodes = await WIKI.db.knex('tree').where('folderPath', '<@', encodeFolderPath(folderPath)).del().returning(['id', 'type'])
const deletedNodes = await WIKI.db.knex('tree').where('folderPath', '<@', folderPath).del().returning(['id', 'type'])
// Delete folders
const deletedFolders = deletedNodes.filter(n => n.type === 'folder').map(n => n.id)
@ -447,7 +445,7 @@ export class Tree extends Model {
if (folder.folderPath) {
const parentPathParts = folder.folderPath.split('.')
const parent = await WIKI.db.knex('tree').where({
folderPath: encodeFolderPath(dropRight(parentPathParts).join('.')),
folderPath: dropRight(parentPathParts).join('.'),
fileName: last(parentPathParts)
}).first()
await WIKI.db.knex('tree').where('id', parent.id).update({

@ -112,18 +112,18 @@
"@quasar/app-vite": "2.3.0",
"@quasar/vite-plugin": "1.10.0",
"@types/lodash": "4.17.20",
"@vue/devtools": "7.7.7",
"@vue/language-plugin-pug": "3.0.4",
"autoprefixer": "10.4.21",
"browserlist": "latest",
"eslint": "9.32.0",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-n": "17.21.0",
"eslint-plugin-n": "17.21.2",
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-vue": "10.3.0",
"eslint-plugin-vue-pug": "1.0.0-alpha.3",
"neostandard": "0.12.2",
"sass": "1.89.2"
"sass": "1.89.2",
"vite-plugin-vue-devtools": "8.0.0"
},
"engines": {
"node": ">= 18.0",

File diff suppressed because it is too large Load Diff

@ -4,6 +4,7 @@ import yaml from 'js-yaml'
import fs from 'node:fs'
import { fileURLToPath } from 'node:url'
import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
@ -55,7 +56,8 @@ export default defineConfig(({ mode }) => {
quasar({
autoImportComponentCase: 'kebab',
sassVariables: '@/css/_theme.scss'
})
}),
vueDevTools()
],
resolve: {
alias: {

Loading…
Cancel
Save