12e073e8cf
mostly because code is being autogenerated by all the AI stuff using this prefix. it's also used in the stack trace.
257 lines
9.4 KiB
JavaScript
257 lines
9.4 KiB
JavaScript
'use strict';
|
|
|
|
exports = module.exports = {
|
|
canEditProfile,
|
|
get,
|
|
setDisplayName,
|
|
setEmail,
|
|
setFallbackEmail,
|
|
getAvatarById,
|
|
setAvatar,
|
|
unsetAvatar,
|
|
setLanguage,
|
|
getBackgroundImage,
|
|
setBackgroundImage,
|
|
unsetBackgroundImage,
|
|
setPassword,
|
|
setTwoFactorAuthenticationSecret,
|
|
enableTwoFactorAuthentication,
|
|
disableTwoFactorAuthentication,
|
|
setNotificationConfig,
|
|
destroyUserSession
|
|
};
|
|
|
|
const assert = require('node:assert'),
|
|
AuditSource = require('../auditsource.js'),
|
|
BoxError = require('../boxerror.js'),
|
|
HttpError = require('@cloudron/connect-lastmile').HttpError,
|
|
HttpSuccess = require('@cloudron/connect-lastmile').HttpSuccess,
|
|
oidcServer = require('../oidcserver.js'),
|
|
safe = require('safetydance'),
|
|
tokens = require('../tokens.js'),
|
|
userDirectory = require('../user-directory.js'),
|
|
users = require('../users.js'),
|
|
settings = require('../settings.js');
|
|
|
|
async function canEditProfile(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
const [error, profileConfig] = await safe(userDirectory.getProfileConfig());
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
if (profileConfig.lockUserProfiles) return next(new HttpError(403, 'admin has disallowed users from editing profiles'));
|
|
|
|
next();
|
|
}
|
|
|
|
async function get(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
const [error, backgroundImage] = await safe(users.getBackgroundImage(req.user));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, {
|
|
id: req.user.id,
|
|
username: req.user.username,
|
|
email: req.user.email,
|
|
fallbackEmail: req.user.fallbackEmail,
|
|
displayName: req.user.displayName,
|
|
twoFactorAuthenticationEnabled: req.user.twoFactorAuthenticationEnabled,
|
|
role: req.user.role,
|
|
source: req.user.source,
|
|
hasBackgroundImage: !!backgroundImage,
|
|
language: req.user.language || await settings.get(settings.LANGUAGE_KEY),
|
|
notificationConfig: req.user.notificationConfig,
|
|
}));
|
|
}
|
|
|
|
async function setEmail(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
|
|
if ('email' in req.body && typeof req.body.email !== 'string') return next(new HttpError(400, 'email must be string'));
|
|
|
|
const [error] = await safe(users.update(req.user, { email: req.body.email }, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204));
|
|
}
|
|
|
|
async function setFallbackEmail(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
|
|
if ('fallbackEmail' in req.body && typeof req.body.fallbackEmail !== 'string') return next(new HttpError(400, 'fallbackEmail must be string'));
|
|
|
|
const [error] = await safe(users.update(req.user, { fallbackEmail: req.body.fallbackEmail }, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204));
|
|
}
|
|
|
|
async function setDisplayName(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
|
|
if ('displayName' in req.body && typeof req.body.displayName !== 'string') return next(new HttpError(400, 'displayName must be string'));
|
|
|
|
const [error] = await safe(users.update(req.user, { displayName: req.body.displayName }, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204));
|
|
}
|
|
|
|
async function setLanguage(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
|
|
if (typeof req.body.language !== 'string') return next(new HttpError(400, 'language must be string'));
|
|
|
|
const [error] = await safe(users.update(req.user, { language: req.body.language }, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204));
|
|
}
|
|
|
|
async function setAvatar(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
assert.strictEqual(typeof req.files, 'object');
|
|
|
|
const avatar = safe.fs.readFileSync(req.files.avatar.path);
|
|
safe.fs.unlinkSync(req.files.avatar.path);
|
|
if (!avatar) return next(BoxError.toHttpError(new BoxError(BoxError.FS_ERROR, safe.error.message)));
|
|
|
|
const [error] = await safe(users.setAvatar(req.user, avatar));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204, {}));
|
|
}
|
|
|
|
async function unsetAvatar(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
const [error] = await safe(users.setAvatar(req.user, null));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204, {}));
|
|
}
|
|
|
|
async function getAvatarById(req, res, next) {
|
|
assert.strictEqual(typeof req.params, 'object');
|
|
|
|
const [userError, user] = await safe(users.get(req.params.identifier));
|
|
if (userError) return next(BoxError.toHttpError(userError));
|
|
if (!user) return next(new HttpError(404, 'no avatar'));
|
|
|
|
const [avatarError, avatar] = await safe(users.getAvatar(user));
|
|
if (avatarError) return next(BoxError.toHttpError(avatarError));
|
|
if (!avatar) return next(new HttpError(404, 'no avatar'));
|
|
|
|
res.set('Content-Type', 'image/png');
|
|
res.status(200).send(avatar);
|
|
}
|
|
|
|
async function setBackgroundImage(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
assert.strictEqual(typeof req.files, 'object');
|
|
|
|
if (!req.files.backgroundImage) return next(new HttpError(400, 'backgroundImage must be provided'));
|
|
const backgroundImage = safe.fs.readFileSync(req.files.backgroundImage.path);
|
|
safe.fs.unlinkSync(req.files.backgroundImage.path);
|
|
if (!backgroundImage) return next(BoxError.toHttpError(new BoxError(BoxError.FS_ERROR, safe.error.message)));
|
|
|
|
const [error] = await safe(users.setBackgroundImage(req.user, backgroundImage));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, {}));
|
|
}
|
|
|
|
async function unsetBackgroundImage(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
const [error] = await safe(users.setBackgroundImage(req.user, null));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, {}));
|
|
}
|
|
|
|
async function getBackgroundImage(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
const [error, backgroundImage] = await safe(users.getBackgroundImage(req.user));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
if (!backgroundImage) return next(new HttpError(404, 'no background set'));
|
|
|
|
res.set('Cache-Control', 'no-cache');
|
|
res.set('Content-Type', 'image/webp');
|
|
|
|
res.status(200).send(backgroundImage);
|
|
}
|
|
|
|
async function setPassword(req, res, next) {
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
if (typeof req.body.newPassword !== 'string') return next(new HttpError(400, 'newPassword must be a string'));
|
|
|
|
const [error] = await safe(users.setPassword(req.user, req.body.newPassword, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204));
|
|
}
|
|
|
|
async function setTwoFactorAuthenticationSecret(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
const [error, result] = await safe(users.setTwoFactorAuthenticationSecret(req.user, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(200, { secret: result.secret, qrcode: result.qrcode }));
|
|
}
|
|
|
|
async function enableTwoFactorAuthentication(req, res, next) {
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
if (!req.body.totpToken || typeof req.body.totpToken !== 'string') return next(new HttpError(400, 'totpToken must be a nonempty string'));
|
|
|
|
const [error] = await safe(users.enableTwoFactorAuthentication(req.user, req.body.totpToken, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204, {}));
|
|
}
|
|
|
|
async function disableTwoFactorAuthentication(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
const [error] = await safe(users.disableTwoFactorAuthentication(req.user, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204, {}));
|
|
}
|
|
|
|
async function setNotificationConfig(req, res, next) {
|
|
assert.strictEqual(typeof req.body, 'object');
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
if (!Array.isArray(req.body.notificationConfig)) return next(new HttpError(400, 'notificationConfig must be an array of strings'));
|
|
if (req.body.notificationConfig.some(k => typeof k !== 'string')) return next(new HttpError(400, 'notificationConfig must be an array of strings'));
|
|
|
|
const [error] = await safe(users.setNotificationConfig(req.user, req.body.notificationConfig, AuditSource.fromRequest(req)));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
next(new HttpSuccess(204, {}));
|
|
}
|
|
|
|
async function destroyUserSession(req, res, next) {
|
|
assert.strictEqual(typeof req.user, 'object');
|
|
|
|
const [error] = await safe(oidcServer.revokeByUsername(req.user.username));
|
|
if (error) return next(BoxError.toHttpError(error));
|
|
|
|
await safe(tokens.del(req.token.id));
|
|
|
|
next(new HttpSuccess(204));
|
|
}
|