| Current Path : /home/deltalab/PMS/partner-manager-backend/graphql/schema/ |
| 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,
};