This commit is contained in:
Paul 2025-04-08 20:50:36 -04:00 committed by GitHub
commit f25139216f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 88 additions and 18 deletions

View File

@ -13,7 +13,8 @@
"start": "node cli-boot-wrapper.js",
"pack": "electron-builder --dir",
"dist": "electron-builder",
"wizard": "npm install --only=prod && node cli-boot-wrapper.js"
"wizard": "npm install --only=prod && node cli-boot-wrapper.js",
"dev": "node --watch-path=./src cli-boot-wrapper.js"
},
"repository": {
"type": "git",

View File

@ -44,7 +44,8 @@ exports.setup = (mstream) => {
req.user = {
vpaths: Object.keys(config.program.folders),
username: 'mstream-user',
admin: true
admin: true,
editFilePrivileges: false
};
return next();

View File

@ -3,7 +3,6 @@ const fs = require('fs').promises;
const fsOld = require('fs');
const busboy = require("busboy");
const Joi = require('joi');
const mkdirp = require('make-dir');
const winston = require('winston');
const fileExplorer = require('../util/file-explorer');
const vpath = require('../util/vpath');
@ -42,12 +41,6 @@ exports.setup = (mstream) => {
// Get vPath Info
const pathInfo = vpath.getVPathInfo(value.directory, req.user);
// Do not allow browsing outside the directory
if (pathInfo.fullPath.substring(0, pathInfo.basePath.length) !== pathInfo.basePath) {
winston.warn(`user '${req.user.username}' attempted to access a directory they don't have access to: ${pathInfo.fullPath}`)
throw new Error('Access to directory not allowed');
}
// get directory contents
const folderContents = await fileExplorer.getDirectoryContents(pathInfo.fullPath, config.program.supportedAudioFiles, value.sort, value.pullMetadata, value.directory, req.user);
@ -91,21 +84,15 @@ exports.setup = (mstream) => {
// Get vPath Info
const pathInfo = vpath.getVPathInfo(req.body.directory, req.user);
// Do not allow browsing outside the directory
if (pathInfo.fullPath.substring(0, pathInfo.basePath.length) !== pathInfo.basePath) {
winston.warn(`user '${req.user.username}' attempted to access a directory they don't have access to: ${pathInfo.fullPath}`)
throw new Error('Access to directory not allowed');
}
res.json(await recursiveFileScan(pathInfo.fullPath, [], pathInfo.relativePath, pathInfo.vpath));
});
mstream.post('/api/v1/file-explorer/upload', (req, res) => {
mstream.post('/api/v1/file-explorer/upload', async (req, res) => {
if (config.program.noUpload === true) { throw new WebError('Uploading Disabled'); }
if (!req.headers['data-location']) { throw new WebError('No Location Provided', 403); }
const pathInfo = vpath.getVPathInfo(decodeURI(req.headers['data-location']), req.user);
mkdirp.sync(pathInfo.fullPath);
await fs.mkdir(pathInfo.fullPath, { recursive: true });
const bb = busboy({ headers: req.headers });
bb.on('file', (fieldname, file, info) => {
@ -134,4 +121,67 @@ exports.setup = (mstream) => {
})
});
});
mstream.post("/api/v1/file-explorer/rename", async (req, res) => {
const schema = Joi.object({
path: Joi.string().required(),
newName: Joi.string().required()
});
joiValidate(schema, req.body);
const pathInfo = vpath.getVPathInfo(req.body.path, req.user);
// check permissions
if (config.program.folders[pathInfo.vpath].edit.rename !== true) { throw new WebError('Rename Disabled'); }
if (req.user.editFilePrivileges.rename !== true) { throw new WebError('Rename Disabled'); }
// check if path exists
await fs.stat(pathInfo.fullPath);
// rename
const newt = path.join(path.dirname(pathInfo.fullPath), req.body.newName);
await fs.rename(pathInfo.fullPath, newt);
res.json({});
});
mstream.delete("/api/v1/file-explorer/delete", async (req, res) => {
const schema = Joi.object({ path: Joi.string().required() });
joiValidate(schema, req.body);
const pathInfo = vpath.getVPathInfo(req.body.path, req.user);
// check permissions
if (config.program.folders[pathInfo.vpath].edit.delete !== true) { throw new WebError('Rename Disabled'); }
if (req.user.editFilePrivileges.delete !== true) { throw new WebError('Rename Disabled'); }
// check if path exists
const stat = await fs.stat(pathInfo.fullPath);
if (stat.isDirectory()) {
await fs.rm(pathInfo.fullPath);
} else {
await fs.unlink(pathInfo.fullPath);
}
// delete
res.json({});
});
// mstream.post("/api/v1/file-explorer/move", async (req, res) => {
// });
mstream.post("/api/v1/file-explorer/mkdir", async (req, res) => {
const schema = Joi.object({ path: Joi.string().required(), newDirName: Joi.string().required() });
joiValidate(schema, req.body);
const pathInfo = vpath.getVPathInfo(req.body.path, req.user);
// check permissions
if (config.program.folders[pathInfo.vpath].edit.mkdir !== true) { throw new WebError('Rename Disabled'); }
if (req.user.editFilePrivileges.mkdir !== true) { throw new WebError('Rename Disabled'); }
await fs.mkdir(pathInfo.fullPath, { recursive: true });
});
}

View File

@ -54,6 +54,13 @@ const federationOptions = Joi.object({
federateUsersMode: Joi.boolean().default(false),
});
const editFileOptions = Joi.object({
rename: Joi.boolean().default(false),
move: Joi.boolean().default(false),
delete: Joi.boolean().default(false),
mkdir: Joi.boolean().default(false),
});
const schema = Joi.object({
address: Joi.string().ip({ cidr: 'forbidden' }).default('::'),
port: Joi.number().default(3000),
@ -81,11 +88,13 @@ const schema = Joi.object({
Joi.object({
root: Joi.string().required(),
type: Joi.string().valid('music', 'audio-books').default('music'),
edit: editFileOptions.default(editFileOptions.validate({}).value),
})
).default({}),
users: Joi.object().pattern(
Joi.string(),
Joi.object({
editFilePrivileges: Joi.boolean().default(false),
password: Joi.string().required(),
admin: Joi.boolean().default(false),
salt: Joi.string().required(),

View File

@ -1,5 +1,6 @@
const path = require('path');
const config = require('../state/config');
const winston = require('winston');
exports.getVPathInfo = (url, user) => {
if (!config.program) { throw new Error('Not Configured'); }
@ -17,10 +18,18 @@ exports.getVPathInfo = (url, user) => {
}
const baseDir = config.program.folders[vpath].root;
const fullPath = path.join(baseDir, path.relative(vpath, url));
// Do not allow browsing outside the directory
if (fullPath.substring(0, baseDir.length) !== baseDir) {
winston.warn(`user '${user.username}' attempted to access a directory they don't have access to: ${fullPath}`)
throw new Error('Access to directory not allowed');
}
return {
vpath: vpath,
basePath: baseDir,
relativePath: path.relative(vpath, url),
fullPath: path.join(baseDir, path.relative(vpath, url))
fullPath: fullPath
};
}