Your IP : 216.73.216.220


Current Path : /home/deltalab/PMS/partner-manager-backend/graphql/schema/
Upload File :
Current File : //home/deltalab/PMS/partner-manager-backend/graphql/schema/user.schema.js

const bcrypt = require('bcrypt');
const auth = require('../../services/auth');
const mail = require('../../services/mail');

// Import dependencies
const { partnerTC } = require('../types/partner.type');
const { userTC } = require('../types/user.type');
const { userTCWithoutPassword } = require('../types/user.type');
const { userModel } = require('../../models/mongoose/user');

function isPasswordValid(str) {
  if (str.search(/[^a-zA-Z0-9\ \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~]/) !== -1) {
    return false;
  }
  if (str.length === 0) {
    return false;
  }
  if (str.length < 12) {
    return false;
  }
  if (str.length > 50) {
    return false;
  }
  if (str.search(/\d/) === -1) {
    return false;
  }
  if (str.search(/[a-z]/) === -1) {
    return false;
  }
  if (str.search(/[A-Z]/) === -1) {
    return false;
  }
  if (str.search(/[\ \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~]/) === -1) {
    return false;
  }
  return true;
}

// RELATIONS =========================================
/**
 * User's Partner relation
 * This relation univocally connects a partner to a user
 */
userTC.addRelation(
  'partner',
  {
    resolver: () => partnerTC.mongooseResolvers.findById(),
    prepareArgs: {
      _id: (source) => source.partnerId,
    },
    projection: { partnerId: true },
  },
);

userTCWithoutPassword.addRelation(
  'partner',
  {
    resolver: () => partnerTC.mongooseResolvers.findById(),
    prepareArgs: {
      _id: (source) => source.partnerId,
    },
    projection: { partnerId: true },
  },
);

// RESOLVERS =========================================
/**
 * FindByMyPartner query resolver;
 * This resolver must be authenticated cause it uses the userId set during the authentication process
 */
userTCWithoutPassword.addResolver({
  kind: 'query',
  name: 'findByMyPartner',
  type: [userTCWithoutPassword],
  args: {},
  resolve: async ({ args }) => {
    // fetch the currently authenticated user
    const currentUser = await userModel.findById(args.decodedAuth.userId).lean();

    // prepare the query by partner id
    const query = {};
    query.partnerId = currentUser.partnerId;

    // execute mongoose query
    const users = await userModel.find(query).lean();

    // return the fetched users
    return users;
  },
});

/**
 * Reset password resolver
 * The partner id is passed for authorization check
 */
userTC.addResolver({
  kind: 'mutation',
  name: 'userResetPassword',
  type: userTC,
  args: {
    userId: 'String!',
    newPassword: 'String!',
    passwordConfirm: 'String!',
    oldPassword: 'String',
    partnerId: 'String',
  },
  resolve: async ({ args }) => {
    // check password validity
    if (!isPasswordValid(args.newPassword) || args.newPassword !== args.passwordConfirm) {
      throw new Error('Invalid password');
    }

    const user = await userModel.findById(args.userId);
    if (!user) throw new Error('Invalid user ID');

    if (user._id == args.decodedAuth.userId) {
      if (!args.oldPassword || !bcrypt.compareSync(args.oldPassword, user.password)) {
        throw new Error('Forbidden');
      }
    }

    const myUser = await userModel.findById(args.decodedAuth.userId);

    // check permissions
    if (myUser.userType === 'STD' && user._id != args.decodedAuth.userId) {
      throw new Error('Forbidden');
    }
    user.password = await bcrypt.hash(args.newPassword, await bcrypt.genSalt());
    user.save();

    return user;
  },
});

/**
 * Login resolver
 */
userTC.addResolver({
  kind: 'mutation',
  name: 'login',
  type: auth.authTC,
  args: {
    username: { type: 'String!' },
    password: { type: 'String!' },
  },
  resolve: async ({ args }) => {
    const { username, password } = args;
    // Fetch user
    const query = {};
    query.username = username;
    const user = await userModel.findOne(query).lean();
    console.log(`User login ${username}`);
    if (!user) {
      // Pretend the user was correct
      throw new Error('Incorrect password');
    }
    // Generate token
    return auth.checkLogin(user, password);
  },
});

// QUERIES ===========================================
const userQuery = {
  ...auth.authenticationRequired(auth.superAdminRequired({
    userByIds: userTCWithoutPassword.mongooseResolvers.findByIds(),
    userMany: userTCWithoutPassword.mongooseResolvers.findMany(),
  })),
  ...auth.authenticationRequired(auth.querySuperAdminOrMyPartnerAdminRequired({
    userById: userTCWithoutPassword.mongooseResolvers.findById(),
  }, true)),
  ...auth.authenticationRequired(auth.partnerOrSuperAdminRequired({
    myPartnerUsers: userTCWithoutPassword.getResolver('findByMyPartner'),
  })),
};

// MUTATIONS =========================================
const userMutation = {
  // Free for all
  login: userTC.getResolver('login'),

  // Requires authentication (use admin access here)
  ...auth.authenticationRequired(
    auth.mutationSuperAdminOrMyPartnerAdminRequired(
      auth.userPasswordHashWrapper({
        // userCreateOne: userTC.mongooseResolvers.createOne(),
        userCreateOne: userCreateOneWrapper(userTC.mongooseResolvers.createOne()),
      }),
    ),
  ),
  ...auth.authenticationRequired(
    auth.superAdminRequired(
      auth.userPasswordHashWrapper({
        userCreateMany: userTC.mongooseResolvers.createMany(),
      }),
    ),
  ),
  ...auth.authenticationRequired(
    auth.mutationSuperAdminOrMyPartnerAdminRequired(
      auth.userSetActualPasswordHash({
        userUpdateById: userTC.mongooseResolvers.updateById(),
        userUpdateOne: userTC.mongooseResolvers.updateOne(),
      }),
    ),
  ),
  ...auth.authenticationRequired(
    auth.superAdminRequired(
      auth.userSetActualPasswordHash({
        userUpdateMany: userTC.mongooseResolvers.updateMany(),
      }),
    ),
  ),
  ...auth.authenticationRequired(
    auth.mutationSuperAdminOrMyPartnerAdminRequired({
      userRemoveById: userTC.mongooseResolvers.removeById(),
      userRemoveOne: userTC.mongooseResolvers.removeOne(),
    }),
  ),
  ...auth.authenticationRequired(
    auth.mutationSuperAdminOrMyPartnerAdminRequired({
      userResetPassword: userTC.getResolver('userResetPassword'),
    }, true),
  ),
  ...auth.authenticationRequired(
    auth.superAdminRequired({
      userRemoveMany: userTC.mongooseResolvers.removeMany(),
    }),
  ),
};

function userCreateOneWrapper(resolver) {
  return resolver.wrapResolve((next) => async (rp) => {
    const payload = await next(rp);
    mail.sendNewUserEmail([rp.args.record.email], rp.args.record.username);
    return payload;
  });
}

// EXPORTS ===========================================
module.exports = {
  userQuery,
  userMutation,
};