import { Body , JsonController , UseBefore , Get , Param , QueryParam , Put , Delete , Authorized , CurrentUser , BadRequestError , ForbiddenError , InternalServerError , NotFoundError , UploadedFile , Post } from 'routing-controllers' ; import passportJwtMiddleware from '../security/passportJwtMiddleware' ; import * as fs from 'fs' ; import * as path from 'path' ; import { IUser } from '../../../shared/models/IUser' ; import { User } from '../models/User' ; import { isNullOrUndefined } from 'util' ; import { errorCodes } from '../config/errorCodes' ; import * as sharp from 'sharp' ; import config from '../config/main' ; import { Course } from '../models/Course' ; import emailService from '../services/EmailService' ; const multer = require ( 'multer' ) ; const uploadOptions = { storage : multer . diskStorage ( { destination : ( req , file , cb ) => { cb ( null , path . join ( config . uploadFolder , 'users' ) ) ; } , filename : ( req , file , cb ) => { const id = req . params . id ; const randomness = '-' + ( Math . floor ( Math . random ( ) * 8999 ) + 1000 ) ; const extPos = file . originalname . lastIndexOf ( '.' ) ; const ext = ( extPos !== - 1 ) ? `.${ file . originalname . substr ( extPos + 1 ) . toLowerCase ( ) }` : '' ; cb ( null , id + randomness + ext ) ; } } ) , } ; function escapeRegex ( text ) { return text . replace ( /[-\/\\^$*+?.()|[\]{}]/g , '\\$&' ) ; } @ JsonController ( '/users' ) @ UseBefore ( passportJwtMiddleware ) export class UserController { api apiName apiGroup apiPermission apiPermission apiParam apiSuccess apiSuccessExample @ Get ( '/' ) @ Authorized ( [ 'teacher' , 'admin' ] ) async getUsers ( @ CurrentUser ( ) currentUser ) { const users = await User . find ( ) ; return users . map ( user => user . forUser ( currentUser ) ) ; } api apiName apiGroup apiPermission apiPermission apiParam apiParam apiParam apiSuccess apiSuccess apiSuccess apiSuccess apiSuccessExample apiError apiError @ Get ( '/members/search' ) @ Authorized ( [ 'teacher' , 'admin' ] ) async searchUser ( @ CurrentUser ( ) currentUser , @ QueryParam ( 'role' ) role , @ QueryParam ( 'query' ) query , @ QueryParam ( 'limit' ) limit ? ) { if ( role !== 'student' && role !== 'teacher' ) { throw new BadRequestError ( 'Method not allowed for this role.' ) ; } query = query . trim ( ) ; if ( isNullOrUndefined ( query ) ) { throw new BadRequestError ( errorCodes . query . empty . code ) ; } const conditions = { } ; const escaped = escapeRegex ( query ) . split ( ' ' ) ; conditions . $or = [ ] ; conditions . $or . push ( { $text : { $search : query } } ) ; escaped . forEach ( elem => { const re = new RegExp ( elem , 'ig' ) ; conditions . $or . push ( { uid : { $regex : re } } ) ; conditions . $or . push ( { email : { $regex : re } } ) ; conditions . $or . push ( { 'profile.firstName' : { $regex : re } } ) ; conditions . $or . push ( { 'profile.lastName' : { $regex : re } } ) ; } ) ; const amountUsers = await User . countDocuments ( { role : role } ) ; const users = await User . find ( conditions , { 'score' : { $meta : 'textScore' } } ) . where ( { role : role } ) . limit ( limit ? limit : Number . MAX_SAFE_INTEGER ) . sort ( { 'score' : { $meta : 'textScore' } } ) ; return { users : users . map ( user => user . forUser ( currentUser ) ) , meta : { count : amountUsers } } ; } api apiName apiGroup apiPermission apiSuccess apiSuccessExample @ Authorized ( [ 'admin' ] ) @ Get ( '/roles/' ) getRoles ( ) { return ( < any > User . schema . path ( 'role' ) ) . enumValues ; } api apiName apiGroup apiParam apiParam apiSuccess apiError apiSuccessExample @ Get ( '/:id([a-fA-F0-9]{24})' ) async getUser ( @ Param ( 'id' ) id , @ CurrentUser ( ) currentUser ? ) { const user = await User . findById ( id ) . populate ( 'progress' ) ; if ( ! user ) { throw new NotFoundError ( `User was not found.` ) ; } return user . forUser ( currentUser ) ; } api apiName apiGroup apiParam apiParam apiParam apiSuccess apiSuccessExample apiError apiError apiError @ Post ( '/picture/:id' ) async addUserPicture ( @ UploadedFile ( 'file' , { options : uploadOptions } ) file , @ Param ( 'id' ) id , @ CurrentUser ( ) currentUser ) { const mimeFamily = file . mimetype . split ( '/' , 1 ) [ 0 ] ; if ( mimeFamily !== 'image' ) { throw new ForbiddenError ( 'Forbidden format of uploaded picture: ' + mimeFamily ) ; } let user = await User . findById ( id ) ; if ( ! user . checkEditableBy ( currentUser ) . editAllowed ) { throw new ForbiddenError ( errorCodes . user . cantChangeUserWithHigherRole . text ) ; } if ( user . profile . picture ) { const oldPicturePath = user . profile . picture . path ; if ( oldPicturePath && fs . existsSync ( oldPicturePath ) ) { fs . unlinkSync ( oldPicturePath ) ; } } const resizedImageBuffer = await sharp ( file . path ) . resize ( config . maxProfileImageWidth , config . maxProfileImageHeight , { fit : 'inside' , withoutEnlargement : true } ) . toBuffer ( { resolveWithObject : true } ) ; fs . writeFileSync ( file . path , resizedImageBuffer . data ) ; user . profile . picture = { _id : null , name : file . filename , alias : file . originalname , path : path . relative ( path . dirname ( config . uploadFolder ) , file . path ) . replace ( /\\\\?/g , '/' ) , size : resizedImageBuffer . info . size } ; try { user = await user . save ( ) ; } catch ( error ) { throw new BadRequestError ( error ) ; } return user . forUser ( currentUser ) ; } api apiName apiGroup apiPermission apiPermission apiPermission apiParam apiParam apiParam apiSuccess apiSuccessExample apiError apiError apiError apiError apiError apiError apiError @ Authorized ( [ 'student' , 'teacher' , 'admin' ] ) @ Put ( '/:id' ) async updateUser ( @ Param ( 'id' ) id , @ Body ( ) newUser , @ CurrentUser ( ) currentUser ) { if ( id === currentUser . _id && currentUser . role !== newUser . role ) { throw new BadRequestError ( errorCodes . user . cantChangeOwnRole . text ) ; } const oldUser = await User . findById ( id ) ; const { userIsAdmin , editAllowed } = oldUser . checkEditableBy ( currentUser ) ; if ( ! editAllowed ) { throw new ForbiddenError ( errorCodes . user . cantChangeUserWithHigherRole . text ) ; } if ( oldUser . uid && newUser . uid === null ) { newUser . uid = oldUser . uid ; } if ( oldUser . role && typeof newUser . role === 'undefined' ) { newUser . role = oldUser . role ; } else if ( typeof User . getEditLevelUnsafe ( newUser ) === 'undefined' ) { throw new BadRequestError ( errorCodes . user . invalidNewUserRole . text ) ; } if ( ! userIsAdmin ) { if ( newUser . role !== oldUser . role ) { throw new ForbiddenError ( errorCodes . user . onlyAdminsCanChangeRoles . text ) ; } if ( newUser . uid !== oldUser . uid ) { throw new ForbiddenError ( errorCodes . user . onlyAdminsCanChangeUids . text ) ; } } if ( typeof newUser . password === 'undefined' || newUser . password . length === 0 ) { delete newUser . password ; } else if ( ! userIsAdmin ) { const isValidPassword = await oldUser . isValidPassword ( newUser . currentPassword ) ; if ( ! isValidPassword ) { throw new BadRequestError ( errorCodes . user . invalidPassword . text ) ; } } { const sameEmail = { $and : [ { 'email' : newUser . email } , { '_id' : { $ne : newUser . _id } } ] } ; const users = await User . find ( sameEmail ) . limit ( 1 ) ; if ( users . length > 0 ) { throw new BadRequestError ( errorCodes . user . emailAlreadyInUse . text ) ; } } const updatedUser = await User . findOneAndUpdate ( { _id : id } , newUser , { new : true } ) ; return updatedUser . forUser ( currentUser ) ; } api apiName apiGroup apiPermission apiPermission apiPermission apiParam apiSuccess apiSuccessExample apiError @ Authorized ( [ 'student' , 'teacher' , 'admin' ] ) @ Delete ( '/:id' ) async deleteUser ( @ Param ( 'id' ) id , @ CurrentUser ( ) currentUser ) { const otherAdmin = await User . findOne ( { $and : [ { 'role' : 'admin' } , { '_id' : { $ne : id } } ] } ) ; if ( id === currentUser . _id && ( currentUser . role === 'teacher' || currentUser . role === 'student' ) ) { try { emailService . sendDeleteRequest ( currentUser , otherAdmin ) ; } catch ( err ) { throw new InternalServerError ( errorCodes . mail . notSend . code ) ; } return { result : true } ; } if ( id === currentUser . _id && currentUser . role === 'admin' ) { if ( otherAdmin === null ) { throw new BadRequestError ( errorCodes . user . noOtherAdmins . text ) ; } } else if ( id !== currentUser . _id && currentUser . role !== 'admin' ) { throw new BadRequestError ( errorCodes . user . cantDeleteOtherUsers . text ) ; } const user = await User . findById ( id ) ; if ( id === currentUser . _id ) { await Course . changeCourseAdminFromUser ( user , otherAdmin ) ; } else { await Course . changeCourseAdminFromUser ( user , currentUser ) ; } await user . remove ( ) ; return { result : true } ; } }