"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureCanManageChannelOrAccount = exports.ensureCanModerateUser = exports.ensureAuthUserOwnsAccountValidator = exports.userAutocompleteValidator = exports.usersVerifyEmailValidator = exports.usersAskSendVerifyEmailValidator = exports.usersResetPasswordValidator = exports.usersAskResetPasswordValidator = exports.usersVideosValidator = exports.usersGetValidator = exports.ensureUserRegistrationAllowedForIP = exports.ensureUserRegistrationAllowed = exports.usersCheckCurrentPasswordFactory = exports.usersVideoRatingValidator = exports.usersUpdateMeValidator = exports.usersUpdateValidator = exports.usersRemoveValidator = exports.usersBlockingValidator = exports.usersRegisterValidator = exports.deleteMeValidator = exports.usersAddValidator = exports.usersListValidator = void 0;
const tslib_1 = require("tslib");
const express_validator_1 = require("express-validator");
const hooks_1 = require("../../lib/plugins/hooks");
const core_utils_1 = require("../../../shared/core-utils");
const misc_1 = require("../../helpers/custom-validators/misc");
const plugins_1 = require("../../helpers/custom-validators/plugins");
const users_1 = require("../../helpers/custom-validators/users");
const video_channels_1 = require("../../helpers/custom-validators/video-channels");
const logger_1 = require("../../helpers/logger");
const theme_utils_1 = require("../../lib/plugins/theme-utils");
const redis_1 = require("../../lib/redis");
const signup_1 = require("../../lib/signup");
const actor_1 = require("../../models/actor/actor");
const shared_1 = require("./shared");
const usersListValidator = [
    (0, express_validator_1.query)('blocked')
        .optional()
        .customSanitizer(misc_1.toBooleanOrNull)
        .isBoolean().withMessage('Should be a valid blocked boolean'),
    (req, res, next) => {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        return next();
    }
];
exports.usersListValidator = usersListValidator;
const usersAddValidator = [
    (0, express_validator_1.body)('username')
        .custom(users_1.isUserUsernameValid)
        .withMessage('Should have a valid username (lowercase alphanumeric characters)'),
    (0, express_validator_1.body)('password')
        .custom(users_1.isUserPasswordValidOrEmpty),
    (0, express_validator_1.body)('email')
        .isEmail(),
    (0, express_validator_1.body)('channelName')
        .optional()
        .custom(video_channels_1.isVideoChannelUsernameValid),
    (0, express_validator_1.body)('videoQuota')
        .custom(users_1.isUserVideoQuotaValid),
    (0, express_validator_1.body)('videoQuotaDaily')
        .custom(users_1.isUserVideoQuotaDailyValid),
    (0, express_validator_1.body)('role')
        .customSanitizer(misc_1.toIntOrNull)
        .custom(users_1.isUserRoleValid),
    (0, express_validator_1.body)('adminFlags')
        .optional()
        .custom(users_1.isUserAdminFlagsValid),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res, { omitBodyLog: true }))
            return;
        if (!(yield (0, shared_1.checkUserNameOrEmailDoesNotAlreadyExist)(req.body.username, req.body.email, res)))
            return;
        const authUser = res.locals.oauth.token.User;
        if (authUser.role !== 0 && req.body.role !== 2) {
            return res.fail({
                status: 403,
                message: 'You can only create users (and not administrators or moderators)'
            });
        }
        if (req.body.channelName) {
            if (req.body.channelName === req.body.username) {
                return res.fail({ message: 'Channel name cannot be the same as user username.' });
            }
            const existing = yield actor_1.ActorModel.loadLocalByName(req.body.channelName);
            if (existing) {
                return res.fail({
                    status: 409,
                    message: `Channel with name ${req.body.channelName} already exists.`
                });
            }
        }
        return next();
    })
];
exports.usersAddValidator = usersAddValidator;
const usersRegisterValidator = [
    (0, express_validator_1.body)('username')
        .custom(users_1.isUserUsernameValid),
    (0, express_validator_1.body)('password')
        .custom(users_1.isUserPasswordValid),
    (0, express_validator_1.body)('email')
        .isEmail(),
    (0, express_validator_1.body)('displayName')
        .optional()
        .custom(users_1.isUserDisplayNameValid),
    (0, express_validator_1.body)('channel.name')
        .optional()
        .custom(video_channels_1.isVideoChannelUsernameValid),
    (0, express_validator_1.body)('channel.displayName')
        .optional()
        .custom(video_channels_1.isVideoChannelDisplayNameValid),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res, { omitBodyLog: true }))
            return;
        if (!(yield (0, shared_1.checkUserNameOrEmailDoesNotAlreadyExist)(req.body.username, req.body.email, res)))
            return;
        const body = req.body;
        if (body.channel) {
            if (!body.channel.name || !body.channel.displayName) {
                return res.fail({ message: 'Channel is optional but if you specify it, channel.name and channel.displayName are required.' });
            }
            if (body.channel.name === body.username) {
                return res.fail({ message: 'Channel name cannot be the same as user username.' });
            }
            const existing = yield actor_1.ActorModel.loadLocalByName(body.channel.name);
            if (existing) {
                return res.fail({
                    status: 409,
                    message: `Channel with name ${body.channel.name} already exists.`
                });
            }
        }
        return next();
    })
];
exports.usersRegisterValidator = usersRegisterValidator;
const usersRemoveValidator = [
    (0, express_validator_1.param)('id')
        .custom(misc_1.isIdValid),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        if (!(yield (0, shared_1.checkUserIdExist)(req.params.id, res)))
            return;
        const user = res.locals.user;
        if (user.username === 'root') {
            return res.fail({ message: 'Cannot remove the root user' });
        }
        return next();
    })
];
exports.usersRemoveValidator = usersRemoveValidator;
const usersBlockingValidator = [
    (0, express_validator_1.param)('id')
        .custom(misc_1.isIdValid),
    (0, express_validator_1.body)('reason')
        .optional()
        .custom(users_1.isUserBlockedReasonValid),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        if (!(yield (0, shared_1.checkUserIdExist)(req.params.id, res)))
            return;
        const user = res.locals.user;
        if (user.username === 'root') {
            return res.fail({ message: 'Cannot block the root user' });
        }
        return next();
    })
];
exports.usersBlockingValidator = usersBlockingValidator;
const deleteMeValidator = [
    (req, res, next) => {
        const user = res.locals.oauth.token.User;
        if (user.username === 'root') {
            return res.fail({ message: 'You cannot delete your root account.' });
        }
        return next();
    }
];
exports.deleteMeValidator = deleteMeValidator;
const usersUpdateValidator = [
    (0, express_validator_1.param)('id').custom(misc_1.isIdValid),
    (0, express_validator_1.body)('password')
        .optional()
        .custom(users_1.isUserPasswordValid),
    (0, express_validator_1.body)('email')
        .optional()
        .isEmail(),
    (0, express_validator_1.body)('emailVerified')
        .optional()
        .isBoolean(),
    (0, express_validator_1.body)('videoQuota')
        .optional()
        .custom(users_1.isUserVideoQuotaValid),
    (0, express_validator_1.body)('videoQuotaDaily')
        .optional()
        .custom(users_1.isUserVideoQuotaDailyValid),
    (0, express_validator_1.body)('pluginAuth')
        .optional()
        .exists(),
    (0, express_validator_1.body)('role')
        .optional()
        .customSanitizer(misc_1.toIntOrNull)
        .custom(users_1.isUserRoleValid),
    (0, express_validator_1.body)('adminFlags')
        .optional()
        .custom(users_1.isUserAdminFlagsValid),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res, { omitBodyLog: true }))
            return;
        if (!(yield (0, shared_1.checkUserIdExist)(req.params.id, res)))
            return;
        const user = res.locals.user;
        if (user.username === 'root' && req.body.role !== undefined && user.role !== req.body.role) {
            return res.fail({ message: 'Cannot change root role.' });
        }
        return next();
    })
];
exports.usersUpdateValidator = usersUpdateValidator;
const usersUpdateMeValidator = [
    (0, express_validator_1.body)('displayName')
        .optional()
        .custom(users_1.isUserDisplayNameValid),
    (0, express_validator_1.body)('description')
        .optional()
        .custom(users_1.isUserDescriptionValid),
    (0, express_validator_1.body)('currentPassword')
        .optional()
        .custom(users_1.isUserPasswordValid),
    (0, express_validator_1.body)('password')
        .optional()
        .custom(users_1.isUserPasswordValid),
    (0, express_validator_1.body)('email')
        .optional()
        .isEmail(),
    (0, express_validator_1.body)('nsfwPolicy')
        .optional()
        .custom(users_1.isUserNSFWPolicyValid),
    (0, express_validator_1.body)('autoPlayVideo')
        .optional()
        .custom(users_1.isUserAutoPlayVideoValid),
    (0, express_validator_1.body)('p2pEnabled')
        .optional()
        .custom(users_1.isUserP2PEnabledValid).withMessage('Should have a valid p2p enabled boolean'),
    (0, express_validator_1.body)('videoLanguages')
        .optional()
        .custom(users_1.isUserVideoLanguages),
    (0, express_validator_1.body)('videosHistoryEnabled')
        .optional()
        .custom(users_1.isUserVideosHistoryEnabledValid).withMessage('Should have a valid videos history enabled boolean'),
    (0, express_validator_1.body)('theme')
        .optional()
        .custom(v => (0, plugins_1.isThemeNameValid)(v) && (0, theme_utils_1.isThemeRegistered)(v)),
    (0, express_validator_1.body)('noInstanceConfigWarningModal')
        .optional()
        .custom(v => (0, users_1.isUserNoModal)(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'),
    (0, express_validator_1.body)('noWelcomeModal')
        .optional()
        .custom(v => (0, users_1.isUserNoModal)(v)).withMessage('Should have a valid noWelcomeModal boolean'),
    (0, express_validator_1.body)('noAccountSetupWarningModal')
        .optional()
        .custom(v => (0, users_1.isUserNoModal)(v)).withMessage('Should have a valid noAccountSetupWarningModal boolean'),
    (0, express_validator_1.body)('autoPlayNextVideo')
        .optional()
        .custom(v => (0, users_1.isUserAutoPlayNextVideoValid)(v)).withMessage('Should have a valid autoPlayNextVideo boolean'),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        const user = res.locals.oauth.token.User;
        if (req.body.password || req.body.email) {
            if (user.pluginAuth !== null) {
                return res.fail({ message: 'You cannot update your email or password that is associated with an external auth system.' });
            }
            if (!req.body.currentPassword) {
                return res.fail({ message: 'currentPassword parameter is missing.' });
            }
            if ((yield user.isPasswordMatch(req.body.currentPassword)) !== true) {
                return res.fail({
                    status: 401,
                    message: 'currentPassword is invalid.'
                });
            }
        }
        if ((0, shared_1.areValidationErrors)(req, res, { omitBodyLog: true }))
            return;
        return next();
    })
];
exports.usersUpdateMeValidator = usersUpdateMeValidator;
const usersGetValidator = [
    (0, express_validator_1.param)('id')
        .custom(misc_1.isIdValid),
    (0, express_validator_1.query)('withStats')
        .optional()
        .isBoolean().withMessage('Should have a valid withStats boolean'),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        if (!(yield (0, shared_1.checkUserIdExist)(req.params.id, res, req.query.withStats)))
            return;
        return next();
    })
];
exports.usersGetValidator = usersGetValidator;
const usersVideoRatingValidator = [
    (0, shared_1.isValidVideoIdParam)('videoId'),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        if (!(yield (0, shared_1.doesVideoExist)(req.params.videoId, res, 'id')))
            return;
        return next();
    })
];
exports.usersVideoRatingValidator = usersVideoRatingValidator;
const usersVideosValidator = [
    (0, express_validator_1.query)('isLive')
        .optional()
        .customSanitizer(misc_1.toBooleanOrNull)
        .custom(misc_1.isBooleanValid).withMessage('Should have a valid isLive boolean'),
    (0, express_validator_1.query)('channelId')
        .optional()
        .customSanitizer(misc_1.toIntOrNull)
        .custom(misc_1.isIdValid),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        if (req.query.channelId && !(yield (0, shared_1.doesVideoChannelIdExist)(req.query.channelId, res)))
            return;
        return next();
    })
];
exports.usersVideosValidator = usersVideosValidator;
const ensureUserRegistrationAllowed = [
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        const allowedParams = {
            body: req.body,
            ip: req.ip
        };
        const allowedResult = yield hooks_1.Hooks.wrapPromiseFun(signup_1.isSignupAllowed, allowedParams, 'filter:api.user.signup.allowed.result');
        if (allowedResult.allowed === false) {
            return res.fail({
                status: 403,
                message: allowedResult.errorMessage || 'User registration is not enabled or user limit is reached.'
            });
        }
        return next();
    })
];
exports.ensureUserRegistrationAllowed = ensureUserRegistrationAllowed;
const ensureUserRegistrationAllowedForIP = [
    (req, res, next) => {
        const allowed = (0, signup_1.isSignupAllowedForCurrentIP)(req.ip);
        if (allowed === false) {
            return res.fail({
                status: 403,
                message: 'You are not on a network authorized for registration.'
            });
        }
        return next();
    }
];
exports.ensureUserRegistrationAllowedForIP = ensureUserRegistrationAllowedForIP;
const usersAskResetPasswordValidator = [
    (0, express_validator_1.body)('email')
        .isEmail(),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        const exists = yield (0, shared_1.checkUserEmailExist)(req.body.email, res, false);
        if (!exists) {
            logger_1.logger.debug('User with email %s does not exist (asking reset password).', req.body.email);
            return res.status(204).end();
        }
        if (res.locals.user.pluginAuth) {
            return res.fail({
                status: 409,
                message: 'Cannot recover password of a user that uses a plugin authentication.'
            });
        }
        return next();
    })
];
exports.usersAskResetPasswordValidator = usersAskResetPasswordValidator;
const usersResetPasswordValidator = [
    (0, express_validator_1.param)('id')
        .custom(misc_1.isIdValid),
    (0, express_validator_1.body)('verificationString')
        .not().isEmpty(),
    (0, express_validator_1.body)('password')
        .custom(users_1.isUserPasswordValid),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        if (!(yield (0, shared_1.checkUserIdExist)(req.params.id, res)))
            return;
        const user = res.locals.user;
        const redisVerificationString = yield redis_1.Redis.Instance.getResetPasswordVerificationString(user.id);
        if (redisVerificationString !== req.body.verificationString) {
            return res.fail({
                status: 403,
                message: 'Invalid verification string.'
            });
        }
        return next();
    })
];
exports.usersResetPasswordValidator = usersResetPasswordValidator;
const usersAskSendVerifyEmailValidator = [
    (0, express_validator_1.body)('email').isEmail().not().isEmpty().withMessage('Should have a valid email'),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        const exists = yield (0, shared_1.checkUserEmailExist)(req.body.email, res, false);
        if (!exists) {
            logger_1.logger.debug('User with email %s does not exist (asking verify email).', req.body.email);
            return res.status(204).end();
        }
        if (res.locals.user.pluginAuth) {
            return res.fail({
                status: 409,
                message: 'Cannot ask verification email of a user that uses a plugin authentication.'
            });
        }
        return next();
    })
];
exports.usersAskSendVerifyEmailValidator = usersAskSendVerifyEmailValidator;
const usersVerifyEmailValidator = [
    (0, express_validator_1.param)('id')
        .isInt().not().isEmpty().withMessage('Should have a valid id'),
    (0, express_validator_1.body)('verificationString')
        .not().isEmpty().withMessage('Should have a valid verification string'),
    (0, express_validator_1.body)('isPendingEmail')
        .optional()
        .customSanitizer(misc_1.toBooleanOrNull),
    (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
        if ((0, shared_1.areValidationErrors)(req, res))
            return;
        if (!(yield (0, shared_1.checkUserIdExist)(req.params.id, res)))
            return;
        const user = res.locals.user;
        const redisVerificationString = yield redis_1.Redis.Instance.getVerifyEmailLink(user.id);
        if (redisVerificationString !== req.body.verificationString) {
            return res.fail({
                status: 403,
                message: 'Invalid verification string.'
            });
        }
        return next();
    })
];
exports.usersVerifyEmailValidator = usersVerifyEmailValidator;
const usersCheckCurrentPasswordFactory = (targetUserIdGetter) => {
    return [
        (0, express_validator_1.body)('currentPassword').optional().custom(misc_1.exists),
        (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
            if ((0, shared_1.areValidationErrors)(req, res))
                return;
            const user = res.locals.oauth.token.User;
            const isAdminOrModerator = user.role === 0 || user.role === 1;
            const targetUserId = (0, core_utils_1.forceNumber)(targetUserIdGetter(req));
            if (isAdminOrModerator && targetUserId !== user.id) {
                return next();
            }
            if (!req.body.currentPassword) {
                return res.fail({
                    status: 400,
                    message: 'currentPassword is missing'
                });
            }
            if ((yield user.isPasswordMatch(req.body.currentPassword)) !== true) {
                return res.fail({
                    status: 403,
                    message: 'currentPassword is invalid.'
                });
            }
            return next();
        })
    ];
};
exports.usersCheckCurrentPasswordFactory = usersCheckCurrentPasswordFactory;
const userAutocompleteValidator = [
    (0, express_validator_1.param)('search')
        .isString()
        .not().isEmpty()
];
exports.userAutocompleteValidator = userAutocompleteValidator;
const ensureAuthUserOwnsAccountValidator = [
    (req, res, next) => {
        const user = res.locals.oauth.token.User;
        if (res.locals.account.id !== user.Account.id) {
            return res.fail({
                status: 403,
                message: 'Only owner of this account can access this resource.'
            });
        }
        return next();
    }
];
exports.ensureAuthUserOwnsAccountValidator = ensureAuthUserOwnsAccountValidator;
const ensureCanManageChannelOrAccount = [
    (req, res, next) => {
        var _a, _b;
        const user = res.locals.oauth.token.user;
        const account = (_b = (_a = res.locals.videoChannel) === null || _a === void 0 ? void 0 : _a.Account) !== null && _b !== void 0 ? _b : res.locals.account;
        const isUserOwner = account.userId === user.id;
        if (!isUserOwner && user.hasRight(13) === false) {
            const message = `User ${user.username} does not have right this channel or account.`;
            return res.fail({
                status: 403,
                message
            });
        }
        return next();
    }
];
exports.ensureCanManageChannelOrAccount = ensureCanManageChannelOrAccount;
const ensureCanModerateUser = [
    (req, res, next) => {
        const authUser = res.locals.oauth.token.User;
        const onUser = res.locals.user;
        if (authUser.role === 0)
            return next();
        if (authUser.role === 1 && onUser.role === 2)
            return next();
        return res.fail({
            status: 403,
            message: 'A moderator can only manage users.'
        });
    }
];
exports.ensureCanModerateUser = ensureCanModerateUser;
//# sourceMappingURL=users.js.map