<commit_before>const userModel = require('../models/user.js');
const yapi = require('../yapi.js');
const baseController = require('./base.js');
const common = require('../utils/commons.js');
const ldap = require('../utils/ldap.js');

const interfaceModel = require('../models/interface.js');
const groupModel = require('../models/group.js');
const projectModel = require('../models/project.js');
const avatarModel = require('../models/avatar.js');

const jwt = require('jsonwebtoken');

class userController extends baseController {
  constructor(ctx) {
    super(ctx);
    this.Model = yapi.getInst(userModel);
  }
  /**
   * 用户登录接口
   * @interface /user/login
   * @method POST
   * @category user
   * @foldnumber 10
   * @param {String} email email名称，不能为空
   * @param  {String} password 密码，不能为空
   * @returns {Object}
   * @example ./api/user/login.json
   */
  async login(ctx) {
    //登录
    let userInst = yapi.getInst(userModel); //创建user实体
    let email = ctx.request.body.email;
    email = (email || '').trim();
    let password = ctx.request.body.password;

    if (!email) {
      return (ctx.body = yapi.commons.resReturn(null, 400, 'email不能为空'));
    }
    if (!password) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '密码不能为空'));
    }

    let result = await userInst.findByEmail(email);

    if (!result) {
      return (ctx.body = yapi.commons.resReturn(null, 404, '该用户不存在'));
    } else if (yapi.commons.generatePassword(password, result.passsalt) === result.password) {
      this.setLoginCookie(result._id, result.passsalt);

      return (ctx.body = yapi.commons.resReturn(
        {
          username: result.username,
          role: result.role,
          uid: result._id,
          email: result.email,
          add_time: result.add_time,
          up_time: result.up_time,
          type: 'site',
          study: result.study
        },
        0,
        'logout success...'
      ));
    } else {
      return (ctx.body = yapi.commons.resReturn(null, 405, '密码错误'));
    }
  }

  /**
   * 退出登录接口
   * @interface /user/logout
   * @method GET
   * @category user
   * @foldnumber 10
   * @returns {Object}
   * @example ./api/user/logout.json
   */

  async logout(ctx) {
    ctx.cookies.set('_yapi_token', null);
    ctx.cookies.set('_yapi_uid', null);
    ctx.body = yapi.commons.resReturn('ok');
  }

  /**
   * 更新
   * @interface /user/up_study
   * @method GET
   * @category user
   * @foldnumber 10
   * @returns {Object}
   * @example
   */

  async upStudy(ctx) {
    let userInst = yapi.getInst(userModel); //创建user实体
    let data = {
      up_time: yapi.commons.time(),
      study: true
    };
    try {
      let result = await userInst.update(this.getUid(), data);
      ctx.body = yapi.commons.resReturn(result);
    } catch (e) {
      ctx.body = yapi.commons.resReturn(null, 401, e.message);
    }
  }

  async loginByToken(ctx) {
    try {
      let ret = await yapi.emitHook('third_login', ctx);
      let login = await this.handleThirdLogin(ret.email, ret.username);
      if (login === true) {
        yapi.commons.log('login success');
        ctx.redirect('/group');
      }
    } catch (e) {
      yapi.commons.log(e.message, 'error');
      ctx.redirect('/');
    }
  }

  /**
   * ldap登录
   * @interface /user/login_by_ldap
   * @method
   * @category user
   * @foldnumber 10
   * @param {String} email email名称，不能为空
   * @param  {String} password 密码，不能为空
   * @returns {Object}
   *
   */
  async getLdapAuth(ctx) {
    try {
      const { email, password } = ctx.request.body;
      // const username = email.split(/\@/g)[0];
      const { info: ldapInfo } = await ldap.ldapQuery(email, password);
      const emailPrefix = email.split(/\@/g)[0];
      const emailPostfix = yapi.WEBCONFIG.ldapLogin.emailPostfix;

      const emailParams =
        ldapInfo[yapi.WEBCONFIG.ldapLogin.emailKey || 'mail'] ||
        (emailPostfix ? emailPrefix + emailPostfix : email);
      const username = ldapInfo[yapi.WEBCONFIG.ldapLogin.usernameKey] || emailPrefix;

      let login = await this.handleThirdLogin(emailParams, username);

      if (login === true) {
        let userInst = yapi.getInst(userModel); //创建user实体
        let result = await userInst.findByEmail(emailParams);
        return (ctx.body = yapi.commons.resReturn(
          {
            username: result.username,
            role: result.role,
            uid: result._id,
            email: result.email,
            add_time: result.add_time,
            up_time: result.up_time,
            type: result.type || 'third',
            study: result.study
          },
          0,
          'logout success...'
        ));
      }
    } catch (e) {
      yapi.commons.log(e.message, 'error');
      return (ctx.body = yapi.commons.resReturn(null, 401, e.message));
    }
  }

  // 处理第三方登录
  async handleThirdLogin(email, username) {
    let user, data, passsalt;
    let userInst = yapi.getInst(userModel);

    try {
      user = await userInst.findByEmail(email);

      // 新建用户信息
      if (!user || !user._id) {
        passsalt = yapi.commons.randStr();
        data = {
          username: username,
          password: yapi.commons.generatePassword(passsalt, passsalt),
          email: email,
          passsalt: passsalt,
          role: 'member',
          add_time: yapi.commons.time(),
          up_time: yapi.commons.time(),
          type: 'third'
        };
        user = await userInst.save(data);
        await this.handlePrivateGroup(user._id, username, email);
        yapi.commons.sendMail({
          to: email,
          contents: `<h3>亲爱的用户：</h3><p>您好，感谢使用YApi平台，你的邮箱账号是：${email}</p>`
        });
      }

      this.setLoginCookie(user._id, user.passsalt);
      return true;
    } catch (e) {
      console.error('third_login:', e.message); // eslint-disable-line
      throw new Error(`third_login: ${e.message}`);
    }
  }

  /**
   * 修改用户密码
   * @interface /user/change_password
   * @method POST
   * @category user
   * @param {Number} uid 用户ID
   * @param {Number} [old_password] 旧密码, 非admin用户必须传
   * @param {Number} password <PASSWORD>
   * @return {Object}
   * @example ./api/user/change_password.json
   */
  async changePassword(ctx) {
    let params = ctx.request.body;
    let userInst = yapi.getInst(userModel);

    if (!params.uid) {
      return (ctx.body = yapi.commons.resReturn(null, 400, 'uid不能为空'));
    }

    if (!params.password) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '密码不能为空'));
    }

    let user = await userInst.findById(params.uid);
    if (this.getRole() !== 'admin' && params.uid != this.getUid()) {
      return (ctx.body = yapi.commons.resReturn(null, 402, '没有权限'));
    }

    if (this.getRole() !== 'admin' || user.role === 'admin') {
      if (!params.old_password) {
        return (ctx.body = yapi.commons.resReturn(null, 400, '旧密码不能为空'));
      }

      if (yapi.commons.generatePassword(params.old_password, user.passsalt) !== user.password) {
        return (ctx.body = yapi.commons.resReturn(null, 402, '旧密码错误'));
      }
    }

    let passsalt = yapi.commons.randStr();
    let data = {
      up_time: yapi.commons.time(),
      password: yapi.commons.generatePassword(params.password, passsalt),
      passsalt: <PASSWORD>
    };
    try {
      let result = await userInst.update(params.uid, data);
      ctx.body = yapi.commons.resReturn(result);
    } catch (e) {
      ctx.body = yapi.commons.resReturn(null, 401, e.message);
    }
  }

  async handlePrivateGroup(uid) {
    var groupInst = yapi.getInst(groupModel);
    await groupInst.save({
      uid: uid,
      group_name: 'User-' + uid,
      add_time: yapi.commons.time(),
      up_time: yapi.commons.time(),
      type: 'private'
    });
  }

  setLoginCookie(uid, passsalt) {
    let token = jwt.sign({ uid: uid }, passsalt, { expiresIn: '7 days' });

    this.ctx.cookies.set('_yapi_token', token, {
      expires: yapi.commons.expireDate(7),
      httpOnly: true
    });
    this.ctx.cookies.set('_yapi_uid', uid, {
      expires: yapi.commons.expireDate(7),
      httpOnly: true
    });
  }

  /**
   * 用户注册接口
   * @interface /user/reg
   * @method POST
   * @category user
   * @foldnumber 10
   * @param {String} email email名称，不能为空
   * @param  {String} password 密码，不能为空
   * @param {String} [username] 用户名
   * @returns {Object}
   * @example ./api/user/login.json
   */
  async reg(ctx) {
    //注册
    if (yapi.WEBCONFIG.closeRegister) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '禁止注册，请联系管理员'));
    }
    let userInst = yapi.getInst(userModel);
    let params = ctx.request.body; //获取请求的参数,检查是否存在用户名和密码

    params = yapi.commons.handleParams(params, {
      username: 'string',
      password: '<PASSWORD>',
      email: 'string'
    });

    if (!params.email) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '邮箱不能为空'));
    }

    if (!params.password) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '密码不能为空'));
    }

    let checkRepeat = await userInst.checkRepeat(params.email); //然后检查是否已经存在该用户

    if (checkRepeat > 0) {
      return (ctx.body = yapi.commons.resReturn(null, 401, '该email已经注册'));
    }

    let passsalt = yapi.commons.randStr();
    let data = {
      username: params.username,
      password: yapi.commons.generatePassword(params.password, passsalt), //加密
      email: params.email,
      passsalt: <PASSWORD>,
      role: 'member',
      add_time: yapi.commons.time(),
      up_time: yapi.commons.time(),
      type: 'site'
    };

    if (!data.username) {
      data.username = data.email.substr(0, data.email.indexOf('@'));
    }

    try {
      let user = await userInst.save(data);

      this.setLoginCookie(user._id, user.passsalt);
      await this.handlePrivateGroup(user._id, user.username, user.email);
      ctx.body = yapi.commons.resReturn({
        uid: user._id,
        email: user.email,
        username: user.username,
        add_time: user.add_time,
        up_time: user.up_time,
        role: 'member',
        type: user.type,
        study: false
      });
      yapi.commons.sendMail({
        to: user.email,
        contents: `<h3>亲爱的用户：</h3><p>您好，感谢使用YApi可视化接口平台,您的账号 ${
          params.email
        } 已经注册成功</p>`
      });
    } catch (e) {
      ctx.body = yapi.commons.resReturn(null, 401, e.message);
    }
  }

  /**
   * 获取用户列表
   * @interface /user/list
   * @method GET
   * @category user
   * @foldnumber 10
   * @param {Number} [page] 分页页码
   * @param {Number} [limit] 分页大小,默认为10条
   * @returns {Object}
   * @example
   */
  async list(ctx) {
    let page = ctx.request.query.page || 1,
      limit = ctx.request.query.limit || 10;

    const userInst = yapi.getInst(userModel);
    try {
      let user = await userInst.listWithPaging(page, limit);
      let count = await userInst.listCount();
      return (ctx.body = yapi.commons.resReturn({
        count: count,
        total: Math.ceil(count / limit),
        list: user
      }));
    } catch (e) {
      return (ctx.body = yapi.commons.resReturn(null, 402, e.message));
    }
  }

  /**
   * 获取用户个人信息
   * @interface /user/find
   * @method GET
   * @param id 用户uid
   * @category user
   * @foldnumber 10
   * @returns {Object}
   * @example
   */
  async findById(ctx) {
    //根据id获取用户信息
    try {
      let userInst = yapi.getInst(userModel);
      let id = ctx.request.query.id;
<commit_msg>fix: 解决请求用户信息接口的越权漏洞<commit_after>const userModel = require('../models/user.js');
const yapi = require('../yapi.js');
const baseController = require('./base.js');
const common = require('../utils/commons.js');
const ldap = require('../utils/ldap.js');

const interfaceModel = require('../models/interface.js');
const groupModel = require('../models/group.js');
const projectModel = require('../models/project.js');
const avatarModel = require('../models/avatar.js');

const jwt = require('jsonwebtoken');

class userController extends baseController {
  constructor(ctx) {
    super(ctx);
    this.Model = yapi.getInst(userModel);
  }
  /**
   * 用户登录接口
   * @interface /user/login
   * @method POST
   * @category user
   * @foldnumber 10
   * @param {String} email email名称，不能为空
   * @param  {String} password 密码，不能为空
   * @returns {Object}
   * @example ./api/user/login.json
   */
  async login(ctx) {
    //登录
    let userInst = yapi.getInst(userModel); //创建user实体
    let email = ctx.request.body.email;
    email = (email || '').trim();
    let password = ctx.request.body.password;

    if (!email) {
      return (ctx.body = yapi.commons.resReturn(null, 400, 'email不能为空'));
    }
    if (!password) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '密码不能为空'));
    }

    let result = await userInst.findByEmail(email);

    if (!result) {
      return (ctx.body = yapi.commons.resReturn(null, 404, '该用户不存在'));
    } else if (yapi.commons.generatePassword(password, result.passsalt) === result.password) {
      this.setLoginCookie(result._id, result.passsalt);

      return (ctx.body = yapi.commons.resReturn(
        {
          username: result.username,
          role: result.role,
          uid: result._id,
          email: result.email,
          add_time: result.add_time,
          up_time: result.up_time,
          type: 'site',
          study: result.study
        },
        0,
        'logout success...'
      ));
    } else {
      return (ctx.body = yapi.commons.resReturn(null, 405, '密码错误'));
    }
  }

  /**
   * 退出登录接口
   * @interface /user/logout
   * @method GET
   * @category user
   * @foldnumber 10
   * @returns {Object}
   * @example ./api/user/logout.json
   */

  async logout(ctx) {
    ctx.cookies.set('_yapi_token', null);
    ctx.cookies.set('_yapi_uid', null);
    ctx.body = yapi.commons.resReturn('ok');
  }

  /**
   * 更新
   * @interface /user/up_study
   * @method GET
   * @category user
   * @foldnumber 10
   * @returns {Object}
   * @example
   */

  async upStudy(ctx) {
    let userInst = yapi.getInst(userModel); //创建user实体
    let data = {
      up_time: yapi.commons.time(),
      study: true
    };
    try {
      let result = await userInst.update(this.getUid(), data);
      ctx.body = yapi.commons.resReturn(result);
    } catch (e) {
      ctx.body = yapi.commons.resReturn(null, 401, e.message);
    }
  }

  async loginByToken(ctx) {
    try {
      let ret = await yapi.emitHook('third_login', ctx);
      let login = await this.handleThirdLogin(ret.email, ret.username);
      if (login === true) {
        yapi.commons.log('login success');
        ctx.redirect('/group');
      }
    } catch (e) {
      yapi.commons.log(e.message, 'error');
      ctx.redirect('/');
    }
  }

  /**
   * ldap登录
   * @interface /user/login_by_ldap
   * @method
   * @category user
   * @foldnumber 10
   * @param {String} email email名称，不能为空
   * @param  {String} password <PASSWORD>
   * @returns {Object}
   *
   */
  async getLdapAuth(ctx) {
    try {
      const { email, password } = ctx.request.body;
      // const username = email.split(/\@/g)[0];
      const { info: ldapInfo } = await ldap.ldapQuery(email, password);
      const emailPrefix = email.split(/\@/g)[0];
      const emailPostfix = yapi.WEBCONFIG.ldapLogin.emailPostfix;

      const emailParams =
        ldapInfo[yapi.WEBCONFIG.ldapLogin.emailKey || 'mail'] ||
        (emailPostfix ? emailPrefix + emailPostfix : email);
      const username = ldapInfo[yapi.WEBCONFIG.ldapLogin.usernameKey] || emailPrefix;

      let login = await this.handleThirdLogin(emailParams, username);

      if (login === true) {
        let userInst = yapi.getInst(userModel); //创建user实体
        let result = await userInst.findByEmail(emailParams);
        return (ctx.body = yapi.commons.resReturn(
          {
            username: result.username,
            role: result.role,
            uid: result._id,
            email: result.email,
            add_time: result.add_time,
            up_time: result.up_time,
            type: result.type || 'third',
            study: result.study
          },
          0,
          'logout success...'
        ));
      }
    } catch (e) {
      yapi.commons.log(e.message, 'error');
      return (ctx.body = yapi.commons.resReturn(null, 401, e.message));
    }
  }

  // 处理第三方登录
  async handleThirdLogin(email, username) {
    let user, data, passsalt;
    let userInst = yapi.getInst(userModel);

    try {
      user = await userInst.findByEmail(email);

      // 新建用户信息
      if (!user || !user._id) {
        passsalt = yapi.commons.randStr();
        data = {
          username: username,
          password: yapi.commons.generatePassword(passsalt, pass<PASSWORD>),
          email: email,
          passsalt: <PASSWORD>,
          role: 'member',
          add_time: yapi.commons.time(),
          up_time: yapi.commons.time(),
          type: 'third'
        };
        user = await userInst.save(data);
        await this.handlePrivateGroup(user._id, username, email);
        yapi.commons.sendMail({
          to: email,
          contents: `<h3>亲爱的用户：</h3><p>您好，感谢使用YApi平台，你的邮箱账号是：${email}</p>`
        });
      }

      this.setLoginCookie(user._id, user.passsalt);
      return true;
    } catch (e) {
      console.error('third_login:', e.message); // eslint-disable-line
      throw new Error(`third_login: ${e.message}`);
    }
  }

  /**
   * 修改用户密码
   * @interface /user/change_password
   * @method POST
   * @category user
   * @param {Number} uid 用户ID
   * @param {Number} [old_password] 旧密码, 非admin用户必须传
   * @param {Number} password <PASSWORD>
   * @return {Object}
   * @example ./api/user/change_password.json
   */
  async changePassword(ctx) {
    let params = ctx.request.body;
    let userInst = yapi.getInst(userModel);

    if (!params.uid) {
      return (ctx.body = yapi.commons.resReturn(null, 400, 'uid不能为空'));
    }

    if (!params.password) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '密码不能为空'));
    }

    let user = await userInst.findById(params.uid);
    if (this.getRole() !== 'admin' && params.uid != this.getUid()) {
      return (ctx.body = yapi.commons.resReturn(null, 402, '没有权限'));
    }

    if (this.getRole() !== 'admin' || user.role === 'admin') {
      if (!params.old_password) {
        return (ctx.body = yapi.commons.resReturn(null, 400, '旧密码不能为空'));
      }

      if (yapi.commons.generatePassword(params.old_password, user.passsalt) !== user.password) {
        return (ctx.body = yapi.commons.resReturn(null, 402, '旧密码错误'));
      }
    }

    let passsalt = yapi.commons.randStr();
    let data = {
      up_time: yapi.commons.time(),
      password: yapi.commons.generatePassword(params.password, passsalt),
      passsalt: pass<PASSWORD>
    };
    try {
      let result = await userInst.update(params.uid, data);
      ctx.body = yapi.commons.resReturn(result);
    } catch (e) {
      ctx.body = yapi.commons.resReturn(null, 401, e.message);
    }
  }

  async handlePrivateGroup(uid) {
    var groupInst = yapi.getInst(groupModel);
    await groupInst.save({
      uid: uid,
      group_name: 'User-' + uid,
      add_time: yapi.commons.time(),
      up_time: yapi.commons.time(),
      type: 'private'
    });
  }

  setLoginCookie(uid, passsalt) {
    let token = jwt.sign({ uid: uid }, passsalt, { expiresIn: '7 days' });

    this.ctx.cookies.set('_yapi_token', token, {
      expires: yapi.commons.expireDate(7),
      httpOnly: true
    });
    this.ctx.cookies.set('_yapi_uid', uid, {
      expires: yapi.commons.expireDate(7),
      httpOnly: true
    });
  }

  /**
   * 用户注册接口
   * @interface /user/reg
   * @method POST
   * @category user
   * @foldnumber 10
   * @param {String} email email名称，不能为空
   * @param  {String} password 密码，不能为空
   * @param {String} [username] 用户名
   * @returns {Object}
   * @example ./api/user/login.json
   */
  async reg(ctx) {
    //注册
    if (yapi.WEBCONFIG.closeRegister) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '禁止注册，请联系管理员'));
    }
    let userInst = yapi.getInst(userModel);
    let params = ctx.request.body; //获取请求的参数,检查是否存在用户名和密码

    params = yapi.commons.handleParams(params, {
      username: 'string',
      password: '<PASSWORD>',
      email: 'string'
    });

    if (!params.email) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '邮箱不能为空'));
    }

    if (!params.password) {
      return (ctx.body = yapi.commons.resReturn(null, 400, '密码不能为空'));
    }

    let checkRepeat = await userInst.checkRepeat(params.email); //然后检查是否已经存在该用户

    if (checkRepeat > 0) {
      return (ctx.body = yapi.commons.resReturn(null, 401, '该email已经注册'));
    }

    let passsalt = yapi.commons.randStr();
    let data = {
      username: params.username,
      password: yapi.commons.generatePassword(params.password, passsalt), //加密
      email: params.email,
      passsalt: <PASSWORD>,
      role: 'member',
      add_time: yapi.commons.time(),
      up_time: yapi.commons.time(),
      type: 'site'
    };

    if (!data.username) {
      data.username = data.email.substr(0, data.email.indexOf('@'));
    }

    try {
      let user = await userInst.save(data);

      this.setLoginCookie(user._id, user.passsalt);
      await this.handlePrivateGroup(user._id, user.username, user.email);
      ctx.body = yapi.commons.resReturn({
        uid: user._id,
        email: user.email,
        username: user.username,
        add_time: user.add_time,
        up_time: user.up_time,
        role: 'member',
        type: user.type,
        study: false
      });
      yapi.commons.sendMail({
        to: user.email,
        contents: `<h3>亲爱的用户：</h3><p>您好，感谢使用YApi可视化接口平台,您的账号 ${
          params.email
        } 已经注册成功</p>`
      });
    } catch (e) {
      ctx.body = yapi.commons.resReturn(null, 401, e.message);
    }
  }

  /**
   * 获取用户列表
   * @interface /user/list
   * @method GET
   * @category user
   * @foldnumber 10
   * @param {Number} [page] 分页页码
   * @param {Number} [limit] 分页大小,默认为10条
   * @returns {Object}
   * @example
   */
  async list(ctx) {
    let page = ctx.request.query.page || 1,
      limit = ctx.request.query.limit || 10;

    const userInst = yapi.getInst(userModel);
    try {
      let user = await userInst.listWithPaging(page, limit);
      let count = await userInst.listCount();
      return (ctx.body = yapi.commons.resReturn({
        count: count,
        total: Math.ceil(count / limit),
        list: user
      }));
    } catch (e) {
      return (ctx.body = yapi.commons.resReturn(null, 402, e.message));
    }
  }

  /**
   * 获取用户个人信息
   * @interface /user/find
   * @method GET
   * @param id 用户uid
   * @category user
   * @foldnumber 10
   * @returns {Object}
   * @example
   */
  async findById(ctx) {
    //根据id获取用户信息
    try {
      let userInst = yapi.getInst(userModel);
      let id = ctx.request.query.id;

      if (this.getRole() !== 'admin' && id != this.getUid()) {
        return (ctx.body = yapi.commons.resReturn(null, 401, '没有权限'));
      }

{'fraction_non_alphanumeric': 0.1510537108948907, 'fraction_numerical': 0.00763558619486016, 'mean_word_length': 2.383525243578388, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3485664', 'n_tokens_mistral': 8271, 'n_tokens_neox': 8177, 'n_words': 1533}