def generateTemporaryPassword(self, params): self.requireParams('email', params) email = params['email'].lower().strip() users = self.model('user').find({'email': email}) if not users.count(): raise RestException('That email is not registered.') for user in users: token = self.model('token').createToken( None, days=1, scope=(TokenScope.USER_AUTH, TokenScope.TEMPORARY_USER_AUTH)) token['userId'] = user['_id'] self.model('token').save(token) base = cherrypy.request.base.rstrip('/') altbase = cherrypy.request.headers.get('X-Forwarded-Host', '') if altbase: base = '%s://%s' % (cherrypy.request.scheme, altbase) url = '%s/#useraccount/%s/token/%s' % (base, str( user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendEmail(to=email, subject='Girder: Temporary Access', text=html) return {'message': 'Sent temporary access email.'}
def testBcc(self): bcc = ('*****@*****.**', '*****@*****.**') mail_utils.sendEmail(to='*****@*****.**', bcc=bcc, subject='hi', text='hi') self.assertTrue(base.mockSmtp.waitForMail()) message = base.mockSmtp.getMail(parse=True) self.assertEqual(message['To'], '*****@*****.**') self.assertEqual(message['Bcc'], ', '.join(bcc))
def inviteToGroup(self, group, params): """Invite the user to join the group.""" self.requireParams('userId', params) user = self.getCurrentUser() level = int(params.get('level', AccessType.READ)) userToInvite = self.model('user').load(id=params['userId'], user=user, level=AccessType.READ, exc=True) # Can only invite into access levels that you yourself have self.model('group').requireAccess(group, user, level) self.model('group').inviteUser(group, userToInvite, level) if params.get('quiet', '').lower() != 'true': html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="Girder: You've been invited to a group") return self.model('group').filter(group, user, accessList=True, requests=True)
def postScore(self, submission, params): # Ensure admin access on the containing challenge phase phase = self.model('phase', 'challenge').load(submission['phaseId'], user=self.getCurrentUser(), exc=True, level=AccessType.ADMIN) submission['score'] = json.loads(cherrypy.request.body.read()) submission = self.model('submission', 'covalic').save(submission) # Delete the scirubg user's job token since the job is now complete. token = self.getCurrentToken() self.model('token').remove(token) user = self.model('user').load(submission['creatorId'], force=True) challenge = self.model('challenge', 'challenge').load(phase['challengeId'], force=True) covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) html = mail_utils.renderTemplate( 'covalic.submissionComplete.mako', { 'phase': phase, 'challenge': challenge, 'submission': submission, 'host': covalicHost }) mail_utils.sendEmail(to=user['email'], subject='Your submission has been scored', text=html) return submission
def testBcc(smtp): bcc = ('*****@*****.**', '*****@*****.**') mail_utils.sendEmail(to='*****@*****.**', bcc=bcc, subject='hi', text='hi') assert smtp.waitForMail() message = smtp.getMail(parse=True) assert message['To'] == '*****@*****.**' assert message['Bcc'] == ', '.join(bcc)
def postScore(self, submission, params): # Ensure admin access on the containing challenge phase phase = self.model('phase', 'challenge').load( submission['phaseId'], user=self.getCurrentUser(), exc=True, level=AccessType.ADMIN) submission['score'] = json.loads(cherrypy.request.body.read()) submission = self.model('submission', 'covalic').save(submission) # Delete the scirubg user's job token since the job is now complete. token = self.getCurrentToken() self.model('token').remove(token) user = self.model('user').load(submission['creatorId'], force=True) challenge = self.model('challenge', 'challenge').load( phase['challengeId'], force=True) covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) html = mail_utils.renderTemplate('covalic.submissionComplete.mako', { 'phase': phase, 'challenge': challenge, 'submission': submission, 'host': covalicHost }) mail_utils.sendEmail( to=user['email'], subject='Your submission has been scored', text=html) return submission
def _uploadComplete(event): """ Called after an upload finishes. We check if our current token is a special authorized upload token, and if so, delete it. TODO we could alternatively keep a reference count inside each token that authorized more than a single upload at a time, and just decrement it here. """ token = getCurrentToken() if token and 'authorizedUploadId' in token: user = User().load(token['userId'], force=True) item = Item().load(event.info['file']['itemId'], force=True) # Save the metadata on the item item['description'] = token['authorizedUploadDescription'] item['authorizedUploadEmail'] = token['authorizedUploadEmail'] Item().save(item) text = mail_utils.renderTemplate( 'authorized_upload.uploadFinished.mako', { 'itemId': item['_id'], 'itemName': item['name'], 'itemDescription': item.get('description', '') }) mail_utils.sendEmail(to=user['email'], subject='Authorized upload complete', text=text) Token().remove(token)
def _uploadComplete(event): """ Called after an upload finishes. We check if our current token is a special authorized upload token, and if so, delete it. TODO we could alternatively keep a reference count inside each token that authorized more than a single upload at a time, and just decrement it here. """ token = getCurrentToken() if 'authorizedUploadId' in token: user = ModelImporter.model('user').load(token['userId'], force=True) item = ModelImporter.model('item').load(event.info['file']['itemId'], force=True) # Save the metadata on the item item['description'] = token['authorizedUploadDescription'] item['authorizedUploadEmail'] = token['authorizedUploadEmail'] ModelImporter.model('item').save(item) text = mail_utils.renderTemplate('authorized_upload.uploadFinished.mako', { 'itemId': item['_id'], 'itemName': item['name'], 'itemDescription': item.get('description', '') }) mail_utils.sendEmail(to=user['email'], subject='Authorized upload complete', text=text) ModelImporter.model('token').remove(token)
def inviteToGroup(self, group, params): """Invite the user to join the group.""" self.requireParams(('userId',), params) user = self.getCurrentUser() level = int(params.get('level', AccessType.READ)) userToInvite = self.model('user').load( id=params['userId'], user=user, level=AccessType.READ, exc=True) # Can only invite into access levels that you yourself have self.model('group').requireAccess(group, user, level) self.model('group').inviteUser(group, userToInvite, level) if params.get('quiet', '').lower() != 'true': html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="Girder: You've been invited to a group") return self.model('group').filter(group, user, accessList=True, requests=True)
def generateTemporaryPassword(self, params): self.requireParams('email', params) email = params['email'].lower().strip() users = self.model('user').find({'email': email}) if not users.count(): raise RestException('That email is not registered.') for user in users: token = self.model('token').createToken(None, days=1, scope=( TokenScope.USER_AUTH, TokenScope.TEMPORARY_USER_AUTH)) token['userId'] = user['_id'] self.model('token').save(token) base = cherrypy.request.base.rstrip('/') altbase = cherrypy.request.headers.get('X-Forwarded-Host', '') if altbase: base = '%s://%s' % (cherrypy.request.scheme, altbase) url = '%s/#useraccount/%s/token/%s' % ( base, str(user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendEmail(to=email, subject='Girder: Temporary Access', text=html) return {'message': 'Sent temporary access email.'}
def _sendApprovedEmail(self, user): text = mail_utils.renderTemplate('accountApproved.mako', { 'user': user, 'url': mail_utils.getEmailUrlPrefix() }) mail_utils.sendEmail(to=user.get('email'), subject='Girder: Account approved', text=text)
def _sendApprovedEmail(self, user): text = mail_utils.renderTemplate('accountApproved.mako', { 'user': user, 'url': mail_utils.getEmailUrlPrefix() }) mail_utils.sendEmail( to=user.get('email'), subject='Girder: Account approved', text=text)
def _sendApprovalEmail(self, user): url = '%s#user/%s' % (mail_utils.getEmailUrlPrefix(), str(user['_id'])) text = mail_utils.renderTemplate('accountApproval.mako', { 'user': user, 'url': url }) mail_utils.sendEmail(toAdmins=True, subject='Girder: Account pending approval', text=text)
def _sendApprovalEmail(self, user): url = '%s/#user/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id'])) text = mail_utils.renderTemplate('accountApproval.mako', { 'user': user, 'url': url }) mail_utils.sendEmail( toAdmins=True, subject='Girder: Account pending approval', text=text)
def _sendVerificationEmail(self, user): token = self.model('token').createToken( user, days=1, scope=TokenScope.EMAIL_VERIFICATION) url = '%s#useraccount/%s/verification/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str( token['_id'])) text = mail_utils.renderTemplate('emailVerification.mako', {'url': url}) mail_utils.sendEmail(to=user.get('email'), subject='Girder: Email verification', text=text)
def _sendVerificationEmail(self, user): token = self.model('token').createToken( user, days=1, scope=TokenScope.EMAIL_VERIFICATION) url = '%s/#useraccount/%s/verification/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str(token['_id'])) text = mail_utils.renderTemplate('emailVerification.mako', { 'url': url }) mail_utils.sendEmail( to=user.get('email'), subject='Girder: Email verification', text=text)
def postScore(self, submission, score, params): # Ensure admin access on the containing challenge phase phase = self.model('phase', 'covalic').load( submission['phaseId'], user=self.getCurrentUser(), exc=True, level=AccessType.ADMIN) # Record whether submission is being re-scored rescoring = 'overallScore' in submission # Save document to trigger computing overall score submission.pop('overallScore', None) submission['score'] = score submission = self.model('submission', 'covalic').save(submission) # Delete the scoring user's job token since the job is now complete. token = self.getCurrentToken() self.model('token').remove(token) user = self.model('user').load(submission['creatorId'], force=True) challenge = self.model('challenge', 'covalic').load( phase['challengeId'], force=True) covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) # Mail user if not rescoring: html = mail_utils.renderTemplate( 'covalic.submissionCompleteUser.mako', { 'phase': phase, 'challenge': challenge, 'submission': submission, 'host': covalicHost }) mail_utils.sendEmail( to=user['email'], subject='Your submission has been scored', text=html) # Mail admins emails = sorted(getPhaseUserEmails( phase, AccessType.WRITE, includeChallengeUsers=True)) html = mail_utils.renderTemplate( 'covalic.submissionCompleteAdmin.mako', { 'user': user, 'phase': phase, 'challenge': challenge, 'submission': submission, 'host': covalicHost }) mail_utils.sendEmail( to=emails, subject='A submission has been scored', text=html) return self._filterScore(phase, submission, user)
def inviteToGroup(self, group, params): """Invite the user to join the group.""" self.requireParams('userId', params) user = self.getCurrentUser() level = int(params.get('level', AccessType.READ)) force = self.boolParam('force', params, default=False) userToInvite = self.model('user').load(id=params['userId'], user=user, level=AccessType.READ, exc=True) if force: if not user.get('admin', False): mustBeAdmin = True addPolicy = self.model('setting').get( SettingKey.ADD_TO_GROUP_POLICY) addGroup = group.get('addAllowed', 'default') if addGroup not in ['no', 'yesadmin', 'yesmod']: addGroup = addPolicy if (self.model('group').hasAccess(group, user, AccessType.ADMIN) and ('mod' in addPolicy or 'admin' in addPolicy) and addGroup.startswith('yes')): mustBeAdmin = False elif (self.model('group').hasAccess( group, user, AccessType.WRITE) and 'mod' in addPolicy and addGroup == 'yesmod'): mustBeAdmin = False if mustBeAdmin: self.requireAdmin(user) self.model('group').addUser(group, userToInvite, level=level) else: # Can only invite into access levels that you yourself have self.model('group').requireAccess(group, user, level) self.model('group').inviteUser(group, userToInvite, level) if not self.boolParam('quiet', params, default=False): html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="Girder: You've been invited to a group") return self.model('group').filter(group, user, accessList=True, requests=True)
def send_created(event): dataset = event.info['dataset'] print(dataset) user = event.info['user'] html = mail_utils.renderTemplate('mdb.dataset_created.mako', { 'dataset': dataset, 'user': user }) email_address = Setting().get(constants.NOTIFICATION_EMAIL) mail_utils.sendEmail(to=email_address, subject='Materials Data Bank: Dataset submitted.', text=html)
def resetPassword(self, email, params): user = self.model('user').findOne({'email': email.lower()}) if user is None: raise RestException('That email is not registered.') randomPass = genToken(length=12) html = mail_utils.renderTemplate('resetPassword.mako', { 'password': randomPass }) mail_utils.sendEmail(to=email, subject='Girder: Password reset', text=html) self.model('user').setPassword(user, randomPass) return {'message': 'Sent password reset email.'}
def resetPassword(self, email): user = self.model('user').findOne({'email': email.lower()}) if user is None: raise RestException('That email is not registered.') randomPass = genToken(length=12) html = mail_utils.renderTemplate('resetPassword.mako', { 'password': randomPass }) mail_utils.sendEmail(to=email, subject='Girder: Password reset', text=html) self.model('user').setPassword(user, randomPass) return {'message': 'Sent password reset email.'}
def send_approved(event): dataset = event.info['dataset'] approver = event.info['approver'] user = User().load(dataset['userId'], force=True) html = mail_utils.renderTemplate('mdb.dataset_approved.mako', { 'dataset': dataset, 'user': user, 'approver': approver }) email_address = user['email'] mail_utils.sendEmail(to=email_address, subject='Materials Data Bank: Dataset approved.', text=html)
def inviteToGroup(self, group, params): self.requireParams('userId', params) user = self.getCurrentUser() level = int(params.get('level', AccessType.READ)) force = self.boolParam('force', params, default=False) groupModel = self.model('group') userToInvite = self.model('user').load( id=params['userId'], user=user, level=AccessType.READ, exc=True) if force: if not user.get('admin', False): mustBeAdmin = True addPolicy = self.model('setting').get( SettingKey.ADD_TO_GROUP_POLICY) addGroup = group.get('addAllowed', 'default') if addGroup not in ['no', 'yesadmin', 'yesmod']: addGroup = addPolicy if (groupModel.hasAccess( group, user, AccessType.ADMIN) and ('mod' in addPolicy or 'admin' in addPolicy) and addGroup.startswith('yes')): mustBeAdmin = False elif (groupModel.hasAccess( group, user, AccessType.WRITE) and 'mod' in addPolicy and addGroup == 'yesmod'): mustBeAdmin = False if mustBeAdmin: self.requireAdmin(user) groupModel.addUser(group, userToInvite, level=level) else: # Can only invite into access levels that you yourself have groupModel.requireAccess(group, user, level) groupModel.inviteUser(group, userToInvite, level) if not self.boolParam('quiet', params, default=False): html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="Girder: You've been invited to a group") group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group
def _sendMail(self, folder, userId, subject, template): """ Sends the specified email template to a single user. :param folder: the curated folder :param userId: the id of the user to email :param subject: the email subject :param template: the name of the mako template to use """ if not userId: return data = dict(folder=folder, curation=folder[CURATION]) text = mail_utils.renderTemplate(template, data) emails = [self._getEmail(userId)] mail_utils.sendEmail(emails, subject, text)
def onJobUpdate(event): """ Hook into job update event so we can look for job failure events and email administrators accordingly. """ if (event.info['job']['type'] == 'covalic_score' and 'status' in event.info['params'] and int(event.info['params']['status']) == JobStatus.ERROR): covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) html = mail_utils.renderTemplate('covalic.submissionError.mako', { 'submissionId': event.info['job']['covalicSubmissionId'], 'host': covalicHost }) mail_utils.sendEmail( toAdmins=True, subject='Submission processing error', text=html)
def inviteUser(self, params): Token = self.model('token') User = self.model('user', 'isic_archive') params = self._decodeParams(params) self.requireParams(['login', 'email', 'firstName', 'lastName'], params) if 'validityPeriod' in params: try: validityPeriod = float(params['validityPeriod']) except ValueError: raise ValidationException('Validity period must be a number.', 'validityPeriod') else: validityPeriod = 60.0 currentUser = self.getCurrentUser() User.requireAdminStudy(currentUser) newUser = User.createUser( login=params['login'], password=None, email=params['email'], firstName=params['firstName'], lastName=params['lastName'] ) token = Token.createToken( newUser, days=validityPeriod, scope=[TokenScope.TEMPORARY_USER_AUTH]) inviteUrl = '%s/#user/%s/rsvp/%s' % ( mail_utils.getEmailUrlPrefix(), newUser['_id'], token['_id']) html = mail_utils.renderTemplate( 'inviteUser.mako', { 'newUser': newUser, 'inviteUrl': inviteUrl, }) mail_utils.sendEmail( to=newUser['email'], subject='ISIC Archive: Invitation', text=html) return { 'newUser': User.filterSummary(newUser, currentUser), 'inviteUrl': inviteUrl }
def onJobUpdate(event): """ Hook into job update event so we can look for job failure events and email administrators accordingly. """ if (event.info['job']['type'] == 'covalic_score' and 'status' in event.info['params'] and int(event.info['params']['status']) == JobStatus.ERROR): covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) html = mail_utils.renderTemplate( 'covalic.submissionError.mako', { 'submissionId': event.info['job']['covalicSubmissionId'], 'host': covalicHost }) mail_utils.sendEmail(toAdmins=True, subject='Submission processing error', text=html)
def requestCreateDatasetPermission(self, params): User = self.model('user', 'isic_archive') Group = self.model('group') currentUser = self.getCurrentUser() resp = {} if User.canCreateDataset(currentUser): resp['message'] = 'Dataset Contributor access granted.', resp['extra'] = 'hasPermission' else: # Request that user join group groupName = 'Dataset Contributors' group = Group.findOne({'name': groupName}) if not group: raise RestException('Could not load group: %s' % groupName) resp['message'] = 'Dataset Contributor access requested. An administrator may contact ' \ 'you via email (at %s) to process your request.' % currentUser['email'] for request in Group.getFullRequestList(group): if request['id'] == currentUser['_id']: # Request for this user is already pending break else: # No request for this user yet Group.joinGroup(group, currentUser) # Send email to group moderators and administrators groupAcl = Group.getFullAccessList(group) groupModeratorEmails = [ getUserEmail(user) for user in groupAcl['users'] if user['level'] >= AccessType.WRITE ] host = mail_utils.getEmailUrlPrefix() html = mail_utils.renderTemplate( 'datasetContributorRequest.mako', { 'user': currentUser, 'group': group, 'host': host, }) mail_utils.sendEmail( to=groupModeratorEmails, subject='ISIC Archive: Dataset Contributor Request', text=html) return resp
def _sendMail(self, folder, userId, subject, template): """ Sends the specified email template to a single user. :param folder: the curated folder :param userId: the id of the user to email :param subject: the email subject :param template: the name of the mako template to use """ if not userId: return data = dict( folder=folder, curation=folder[CURATION]) text = mail_utils.renderTemplate(template, data) emails = [self._getEmail(userId)] mail_utils.sendEmail(emails, subject, text)
def generateTemporaryPassword(self, email): user = self.model('user').findOne({'email': email.lower()}) if not user: raise RestException('That email is not registered.') token = self.model('token').createToken( user, days=1, scope=TokenScope.TEMPORARY_USER_AUTH) url = '%s#useraccount/%s/token/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendEmail(to=email, subject='Girder: Temporary access', text=html) return {'message': 'Sent temporary access email.'}
def resetPassword(self, params): self.requireParams('email', params) email = params['email'].lower().strip() cursor = self.model('user').find({'email': email}, limit=1) if cursor.count() == 0: raise RestException('That email is not registered.') user = cursor.next() randomPass = genToken(length=12) html = mail_utils.renderTemplate('resetPassword.mako', {'password': randomPass}) mail_utils.sendEmail(to=email, subject='Girder: Password reset', text=html) self.model('user').setPassword(user, randomPass) return {'message': 'Sent password reset email.'}
def resetPassword(self, params): self.requireParams(('email',), params) email = params['email'].lower().strip() cursor = self.model('user').find({'email': email}, limit=1) if cursor.count() == 0: raise RestException('That email is not registered.') user = cursor.next() randomPass = genToken(length=12) html = mail_utils.renderTemplate('resetPassword.mako', { 'password': randomPass }) mail_utils.sendEmail(to=email, subject='Girder: Password reset', text=html) self.model('user').setPassword(user, randomPass) return {'message': 'Sent password reset email.'}
def generateTemporaryPassword(self, email, params): user = self.model('user').findOne({'email': email.lower()}) if not user: raise RestException('That email is not registered.') token = self.model('token').createToken( user, days=1, scope=TokenScope.TEMPORARY_USER_AUTH) url = '%s#useraccount/%s/token/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str(token['_id'])) html = mail_utils.renderTemplate('temporaryAccess.mako', { 'url': url, 'token': str(token['_id']) }) mail_utils.sendEmail(to=email, subject='Girder: Temporary access', text=html) return {'message': 'Sent temporary access email.'}
def sendEmailToGroup(groupName, templateFilename, templateParams, subject=None): """ Send a single email with all members of a group as the recipients. :param groupName: The name of the group. :param templateFilename: The name of the Make template file used to format the email. :param templateParams: The parameters with which to render the template. :param subject: The subject line of the email. """ Group = ModelImporter.model('group') group = Group.findOne({'name': groupName}) if not group: raise Exception('Could not load group: %s.' % groupName) emails = [member['email'] for member in Group.listMembers(group)] if emails: html = mail_utils.renderTemplate(templateFilename, templateParams) mail_utils.sendEmail(to=emails, subject=subject, text=html)
def _ingestZip(self, dataset, zipFile, batch, user, sendMail): """ Ingest images from a ZIP file into a dataset. The images are extracted to a "Pre-review" folder within the dataset folder. """ prereviewFolder = Folder().createFolder( parent=self.imagesFolder(dataset), name='Pre-review', parentType='folder', creator=user, public=False, reuseExisting=True) # Process zip file # TODO: gracefully clean up after exceptions in handleZip self._handleZip(dataset, batch, prereviewFolder, user, zipFile) # Send email confirmations if sendMail: host = mail_utils.getEmailUrlPrefix() params = { 'group': False, 'host': host, 'dataset': dataset, # We intentionally leak full user details here, even though all # email recipients may not have access permissions to the user 'user': user, 'batch': batch, } subject = 'ISIC Archive: Dataset Upload Confirmation' templateFilename = 'ingestDatasetConfirmation.mako' # Mail user html = mail_utils.renderTemplate(templateFilename, params) mail_utils.sendEmail(to=user['email'], subject=subject, text=html) # Mail 'Dataset QC Reviewers' group params['group'] = True isic_mail_utils.sendEmailToGroup( groupName='Dataset QC Reviewers', templateFilename=templateFilename, templateParams=params, subject=subject)
def inviteToGroup(self, group, userToInvite, level, quiet, force): groupModel = self._model user = self.getCurrentUser() if force: if not user['admin']: mustBeAdmin = True addPolicy = Setting().get(SettingKey.ADD_TO_GROUP_POLICY) addGroup = group.get('addAllowed', 'default') if addGroup not in ['no', 'yesadmin', 'yesmod']: addGroup = addPolicy if (groupModel.hasAccess( group, user, AccessType.ADMIN) and ('mod' in addPolicy or 'admin' in addPolicy) and addGroup.startswith('yes')): mustBeAdmin = False elif (groupModel.hasAccess( group, user, AccessType.WRITE) and 'mod' in addPolicy and addGroup == 'yesmod'): mustBeAdmin = False if mustBeAdmin: self.requireAdmin(user) groupModel.addUser(group, userToInvite, level=level) else: # Can only invite into access levels that you yourself have groupModel.requireAccess(group, user, level) groupModel.inviteUser(group, userToInvite, level) if not quiet: html = mail_utils.renderTemplate('groupInvite.mako', { 'userToInvite': userToInvite, 'user': user, 'group': group }) mail_utils.sendEmail( to=userToInvite['email'], text=html, subject="%s: You've been invited to a group" % Setting().get(SettingKey.BRAND_NAME) ) group['access'] = groupModel.getFullAccessList(group) group['requests'] = list(groupModel.getFullRequestList(group)) return group
def sendEmailToGroup(groupName, templateFilename, templateParams, subject=None): """ Send a single email with all members of a group as the recipients. :param groupName: The name of the group. :param templateFilename: The name of the Make template file used to format the email. :param templateParams: The parameters with which to render the template. :param subject: The subject line of the email. """ group = Group().findOne({'name': groupName}) if not group: raise Exception('Could not load group: %s.' % groupName) emails = [member['email'] for member in Group().listMembers(group)] if emails: html = mail_utils.renderTemplate(templateFilename, templateParams) mail_utils.sendEmail(to=emails, subject=subject, text=html)
def requestCreateDatasetPermission(self, params): currentUser = self.getCurrentUser() resp = {} if User().canCreateDataset(currentUser): resp['message'] = 'Dataset Contributor access granted.', resp['extra'] = 'hasPermission' else: # Request that user join group groupName = 'Dataset Contributors' group = Group().findOne({'name': groupName}) if not group: raise RestException('Could not load group: %s' % groupName) resp['message'] = 'Dataset Contributor access requested. An administrator may contact ' \ 'you via email (at %s) to process your request.' % currentUser['email'] for request in Group().getFullRequestList(group): if request['id'] == currentUser['_id']: # Request for this user is already pending break else: # No request for this user yet Group().joinGroup(group, currentUser) # Send email to group moderators and administrators groupAcl = Group().getFullAccessList(group) groupModeratorEmails = [ getUserEmail(user) for user in groupAcl['users'] if user['level'] >= AccessType.WRITE ] if groupModeratorEmails: host = mail_utils.getEmailUrlPrefix() html = mail_utils.renderTemplate( 'datasetContributorRequest.mako', { 'user': currentUser, 'group': group, 'host': host, }) mail_utils.sendEmail( to=groupModeratorEmails, subject='ISIC Archive: Dataset Contributor Request', text=html) return resp
def inviteUser(self, params): params = self._decodeParams(params) self.requireParams(['login', 'email', 'firstName', 'lastName'], params) if 'validityPeriod' in params: try: validityPeriod = float(params['validityPeriod']) except ValueError: raise ValidationException('Validity period must be a number.', 'validityPeriod') else: validityPeriod = 60.0 currentUser = self.getCurrentUser() User().requireAdminStudy(currentUser) newUser = User().createUser(login=params['login'], password=None, email=params['email'], firstName=params['firstName'], lastName=params['lastName']) token = Token().createToken(newUser, days=validityPeriod, scope=[TokenScope.TEMPORARY_USER_AUTH]) inviteUrl = '%s/#user/%s/rsvp/%s' % (mail_utils.getEmailUrlPrefix(), newUser['_id'], token['_id']) html = mail_utils.renderTemplate('inviteUser.mako', { 'newUser': newUser, 'inviteUrl': inviteUrl, }) mail_utils.sendEmail(to=newUser['email'], subject='ISIC Archive: Invitation', text=html) return { 'newUser': User().filterSummary(newUser, currentUser), 'inviteUrl': inviteUrl }
def testEmailAdmins(self): self.assertTrue(base.mockSmtp.isMailQueueEmpty()) admin1, admin2 = [ self.model('user').createUser(firstName='Admin%d' % i, lastName='Admin', login='******' % i, password='******', admin=True, email='*****@*****.**' % i) for i in range(2) ] # Set the email from address self.model('setting').set(SettingKey.EMAIL_FROM_ADDRESS, '*****@*****.**') # Test sending email to admin users mail_utils.sendEmail(text='hello', toAdmins=True) self.assertTrue(base.mockSmtp.waitForMail()) message = base.mockSmtp.getMail(parse=True) self.assertEqual(message['subject'], '[no subject]') self.assertEqual(message['content-type'], 'text/html; charset="utf-8"') self.assertEqual(message['to'], '[email protected], [email protected]') self.assertEqual(message['from'], '*****@*****.**') self.assertEqual(message.get_payload(decode=True), b'hello') # Test sending email to multiple recipients self.assertTrue(base.mockSmtp.isMailQueueEmpty()) mail_utils.sendEmail(to=('*****@*****.**', '*****@*****.**'), text='world', subject='Email alert') self.assertTrue(base.mockSmtp.waitForMail()) message = base.mockSmtp.getMail(parse=True) self.assertEqual(message['subject'], 'Email alert') self.assertEqual(message['to'], '[email protected], [email protected]') self.assertEqual(message['from'], '*****@*****.**') self.assertEqual(message.get_payload(decode=True), b'world') # Pass nonsense in the "to" field, check exception x = 0 try: mail_utils.sendEmail(text='hello', to=None) except Exception as e: x = 1 self.assertEqual( e.args[0], 'You must specify email recipients via "to" or ' '"bcc", or use toAdmins=True.') self.assertEqual(x, 1)
def testEmailAdmins(self): self.assertTrue(base.mockSmtp.isMailQueueEmpty()) admin1, admin2 = [ self.model('user').createUser(firstName='Admin%d' % i, lastName='Admin', login='******' % i, password='******', admin=True, email='*****@*****.**' % i) for i in range(2) ] # Set the email from address self.model('setting').set(SettingKey.EMAIL_FROM_ADDRESS, '*****@*****.**') # Test sending email to admin users mail_utils.sendEmail(text='hello', toAdmins=True) self.assertTrue(base.mockSmtp.waitForMail()) lines = base.mockSmtp.getMail().strip().splitlines() self.assertTrue('Subject: [no subject]' in lines) self.assertTrue('To: [email protected], [email protected]' in lines) self.assertTrue('From: [email protected]' in lines) self.assertEqual(lines[-1], 'hello') # Test sending email to multiple recipients self.assertTrue(base.mockSmtp.isMailQueueEmpty()) mail_utils.sendEmail(to=('*****@*****.**', '*****@*****.**'), text='world', subject='Email alert') self.assertTrue(base.mockSmtp.waitForMail()) lines = base.mockSmtp.getMail().strip().splitlines() self.assertTrue('Subject: Email alert' in lines) self.assertTrue('To: [email protected], [email protected]' in lines) self.assertTrue('From: [email protected]' in lines) self.assertEqual(lines[-1], 'world') # Pass nonsense in the "to" field, check exception x = 0 try: mail_utils.sendEmail(text='hello', to=None) except Exception as e: x = 1 self.assertEqual( e.args[0], 'You must specify a "to" address or list of ' 'addresses or set toAdmins=True when calling sendEmail.') self.assertEqual(x, 1)
def testEmailAdmins(self): self.assertTrue(base.mockSmtp.isMailQueueEmpty()) admin1, admin2 = [self.model('user').createUser( firstName='Admin%d' % i, lastName='Admin', login='******' % i, password='******', admin=True, email='*****@*****.**' % i) for i in range(2)] # Set the email from address self.model('setting').set(SettingKey.EMAIL_FROM_ADDRESS, '*****@*****.**') # Test sending email to admin users mail_utils.sendEmail(text='hello', toAdmins=True) self.assertTrue(base.mockSmtp.waitForMail()) message = base.mockSmtp.getMail(parse=True) self.assertEqual(message['subject'], '[no subject]') self.assertEqual(message['content-type'], 'text/html; charset="utf-8"') self.assertEqual(message['to'], '[email protected], [email protected]') self.assertEqual(message['from'], '*****@*****.**') self.assertEqual(message.get_payload(decode=True), b'hello') # Test sending email to multiple recipients self.assertTrue(base.mockSmtp.isMailQueueEmpty()) mail_utils.sendEmail(to=('*****@*****.**', '*****@*****.**'), text='world', subject='Email alert') self.assertTrue(base.mockSmtp.waitForMail()) message = base.mockSmtp.getMail(parse=True) self.assertEqual(message['subject'], 'Email alert') self.assertEqual(message['to'], '[email protected], [email protected]') self.assertEqual(message['from'], '*****@*****.**') self.assertEqual(message.get_payload(decode=True), b'world') # Pass nonsense in the "to" field, check exception x = 0 try: mail_utils.sendEmail(text='hello', to=None) except Exception as e: x = 1 self.assertEqual( e.args[0], 'You must specify email recipients via "to" or ' '"bcc", or use toAdmins=True.') self.assertEqual(x, 1)
def testEmailAdmins(self): self.assertTrue(base.mockSmtp.isMailQueueEmpty()) admin1, admin2 = [self.model('user').createUser( firstName='Admin%d' % i, lastName='Admin', login='******' % i, password='******', admin=True, email='*****@*****.**' % i) for i in range(2)] # Set the email from address self.model('setting').set(SettingKey.EMAIL_FROM_ADDRESS, '*****@*****.**') # Test sending email to admin users mail_utils.sendEmail(text='hello', toAdmins=True) self.assertTrue(base.mockSmtp.waitForMail()) lines = base.mockSmtp.getMail().strip().splitlines() self.assertTrue('Subject: [no subject]' in lines) self.assertTrue('To: [email protected], [email protected]' in lines) self.assertTrue('From: [email protected]' in lines) self.assertEqual(lines[-1], 'hello') # Test sending email to multiple recipients self.assertTrue(base.mockSmtp.isMailQueueEmpty()) mail_utils.sendEmail(to=('*****@*****.**', '*****@*****.**'), text='world', subject='Email alert') self.assertTrue(base.mockSmtp.waitForMail()) lines = base.mockSmtp.getMail().strip().splitlines() self.assertTrue('Subject: Email alert' in lines) self.assertTrue('To: [email protected], [email protected]' in lines) self.assertTrue('From: [email protected]' in lines) self.assertEqual(lines[-1], 'world') # Pass nonsense in the "to" field, check exception x = 0 try: mail_utils.sendEmail(text='hello', to=None) except Exception as e: x = 1 self.assertEqual( e.message, 'You must specify a "to" address or list of ' 'addresses or set toAdmins=True when calling sendEmail.') self.assertEqual(x, 1)
def testEmailAdmins(smtp): assert smtp.isMailQueueEmpty() for i in range(2): # Create 2 admin users to test sending mail to admins User().createUser(firstName='Admin%d' % i, lastName='Admin', login='******' % i, password='******', admin=True, email='*****@*****.**' % i) # Set the email from address Setting().set(SettingKey.EMAIL_FROM_ADDRESS, '*****@*****.**') # Test sending email to admin users mail_utils.sendEmail(text='hello', toAdmins=True) assert smtp.waitForMail() message = smtp.getMail(parse=True) assert message['subject'] == '[no subject]' assert message['content-type'] == 'text/html; charset="utf-8"' assert message['to'] == '[email protected], [email protected]' assert message['from'] == '*****@*****.**' assert message.get_payload(decode=True) == b'hello' # Test sending email to multiple recipients assert smtp.isMailQueueEmpty() mail_utils.sendEmail(to=('*****@*****.**', '*****@*****.**'), text='world', subject='Email alert') assert smtp.waitForMail() message = smtp.getMail(parse=True) assert message['subject'] == 'Email alert' assert message['to'] == '[email protected], [email protected]' assert message['from'] == '*****@*****.**' assert message.get_payload(decode=True) == b'world' # Pass nonsense in the "to" field, check exception msg = 'You must specify email recipients via "to" or "bcc", or use toAdmins=True.$' with pytest.raises(Exception, match=msg): mail_utils.sendEmail(text='hello', to=None)
def testEmailAdmins(smtp): assert smtp.isMailQueueEmpty() for i in range(2): # Create 2 admin users to test sending mail to admins User().createUser( firstName='Admin%d' % i, lastName='Admin', login='******' % i, password='******', admin=True, email='*****@*****.**' % i) # Set the email from address Setting().set(SettingKey.EMAIL_FROM_ADDRESS, '*****@*****.**') # Test sending email to admin users mail_utils.sendEmail(text='hello', toAdmins=True) assert smtp.waitForMail() message = smtp.getMail(parse=True) assert message['subject'] == '[no subject]' assert message['content-type'] == 'text/html; charset="utf-8"' assert message['to'] == '[email protected], [email protected]' assert message['from'] == '*****@*****.**' assert message.get_payload(decode=True) == b'hello' # Test sending email to multiple recipients assert smtp.isMailQueueEmpty() mail_utils.sendEmail(to=('*****@*****.**', '*****@*****.**'), text='world', subject='Email alert') assert smtp.waitForMail() message = smtp.getMail(parse=True) assert message['subject'] == 'Email alert' assert message['to'] == '[email protected], [email protected]' assert message['from'] == '*****@*****.**' assert message.get_payload(decode=True) == b'world' # Pass nonsense in the "to" field, check exception msg = 'You must specify email recipients via "to" or "bcc", or use toAdmins=True.$' with pytest.raises(Exception, match=msg): mail_utils.sendEmail(text='hello', to=None)
def ingestDataset(self, zipFile, user, name, owner, description, license, signature, anonymous, attribution, sendMail=False): """ Ingest an uploaded dataset from a .zip file of images. The images are extracted to a "Pre-review" folder within a new dataset folder. """ # Create dataset folder dataset = self.createDataset(name, description, user) # Set dataset metadata, including license info dataset['meta'] = { 'owner': owner, 'signature': signature, 'anonymous': anonymous, 'attribution': attribution, 'license': license, 'metadataFiles': [] } dataset = Dataset().save(dataset) prereviewFolder = Folder().createFolder(parent=dataset, name='Pre-review', parentType='folder', creator=user, public=False) prereviewFolder = Folder().copyAccessPolicies(dataset, prereviewFolder, save=True) # Process zip file # TODO: gracefully clean up after exceptions in handleZip self._handleZip(prereviewFolder, user, zipFile) # Send email confirmations if sendMail: host = mail_utils.getEmailUrlPrefix() params = { 'group': False, 'host': host, # We intentionally leak full user details here, even though all # email recipients may not have access permissions to the user 'user': user, 'name': name, 'owner': owner, 'description': description, 'license': license, 'signature': signature, 'attribution': 'Anonymous' if anonymous else attribution } subject = 'ISIC Archive: Dataset Upload Confirmation' templateFilename = 'ingestDatasetConfirmation.mako' # Mail user html = mail_utils.renderTemplate(templateFilename, params) mail_utils.sendEmail(to=user['email'], subject=subject, text=html) # Mail 'Dataset QC Reviewers' group params['group'] = True isic_mail_utils.sendEmailToGroup(groupName='Dataset QC Reviewers', templateFilename=templateFilename, templateParams=params, subject=subject) return dataset
def testUnicodeEmail(self): text = u'Contains unic\xf8de \u0420\u043e\u0441\u0441\u0438\u044f' mail_utils.sendEmail(to='*****@*****.**', subject=text, text=text) self.assertTrue(base.mockSmtp.waitForMail()) message = base.mockSmtp.getMail(parse=True) self.assertEqual(message.get_payload(decode=True), text.encode('utf8'))
def onJobUpdate(event): """ Hook into job update event so we can look for job failure events and email the user and challenge/phase administrators accordingly. Here, an administrator is defined to be a user with WRITE access or above. """ isErrorStatus = False try: isErrorStatus = int(event.info['params'].get('status')) == JobStatus.ERROR except (ValueError, TypeError): pass if (event.info['job']['type'] == 'covalic_score' and isErrorStatus): covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) # Create minimal log that contains only Covalic errors. # Use full log if no Covalic-specific errors are found. # Fetch log from model, because log in event may not be up-to-date. job = Job().load( event.info['job']['_id'], includeLog=True, force=True) log = job.get('log') minimalLog = None if log: log = ''.join(log) minimalLog = '\n'.join([line[len(JOB_LOG_PREFIX):].strip() for line in log.splitlines() if line.startswith(JOB_LOG_PREFIX)]) if not minimalLog: minimalLog = log submission = Submission().load( event.info['job']['covalicSubmissionId']) phase = Phase().load( submission['phaseId'], force=True) challenge = Challenge().load( phase['challengeId'], force=True) user = User().load( event.info['job']['userId'], force=True) rescoring = job.get('rescoring', False) # Mail admins, include full log emails = sorted(getPhaseUserEmails( phase, AccessType.WRITE, includeChallengeUsers=True)) html = mail_utils.renderTemplate('covalic.submissionErrorAdmin.mako', { 'submission': submission, 'challenge': challenge, 'phase': phase, 'user': user, 'host': covalicHost, 'log': log }) mail_utils.sendEmail( to=emails, subject='Submission processing error', text=html) # Mail user, include minimal log if not rescoring: html = mail_utils.renderTemplate('covalic.submissionErrorUser.mako', { 'submission': submission, 'challenge': challenge, 'phase': phase, 'host': covalicHost, 'log': minimalLog }) mail_utils.sendEmail( to=user['email'], subject='Submission processing error', text=html)
def testUnicodeEmail(smtp): text = u'Contains unic\xf8de \u0420\u043e\u0441\u0441\u0438\u044f' mail_utils.sendEmail(to='*****@*****.**', subject=text, text=text) assert smtp.waitForMail() message = smtp.getMail(parse=True) assert message.get_payload(decode=True) == text.encode('utf8')
def onJobUpdate(event): """ Hook into job update event so we can look for job failure events and email the user and challenge/phase administrators accordingly. Here, an administrator is defined to be a user with WRITE access or above. """ isErrorStatus = False try: isErrorStatus = int( event.info['params'].get('status')) == JobStatus.ERROR except (ValueError, TypeError): pass if (event.info['job']['type'] == 'covalic_score' and isErrorStatus): covalicHost = posixpath.dirname(mail_utils.getEmailUrlPrefix()) # Create minimal log that contains only Covalic errors. # Use full log if no Covalic-specific errors are found. # Fetch log from model, because log in event may not be up-to-date. job = ModelImporter.model('job', 'jobs').load(event.info['job']['_id'], includeLog=True, force=True) log = job.get('log') minimalLog = None if log: log = ''.join(log) minimalLog = '\n'.join([ line[len(JOB_LOG_PREFIX):].strip() for line in log.splitlines() if line.startswith(JOB_LOG_PREFIX) ]) if not minimalLog: minimalLog = log submission = ModelImporter.model('submission', 'covalic').load( event.info['job']['covalicSubmissionId']) phase = ModelImporter.model('phase', 'covalic').load(submission['phaseId'], force=True) challenge = ModelImporter.model('challenge', 'covalic').load(phase['challengeId'], force=True) user = ModelImporter.model('user').load(event.info['job']['userId'], force=True) rescoring = job.get('rescoring', False) # Mail admins, include full log emails = sorted( getPhaseUserEmails(phase, AccessType.WRITE, includeChallengeUsers=True)) html = mail_utils.renderTemplate( 'covalic.submissionErrorAdmin.mako', { 'submission': submission, 'challenge': challenge, 'phase': phase, 'user': user, 'host': covalicHost, 'log': log }) mail_utils.sendEmail(to=emails, subject='Submission processing error', text=html) # Mail user, include minimal log if not rescoring: html = mail_utils.renderTemplate( 'covalic.submissionErrorUser.mako', { 'submission': submission, 'challenge': challenge, 'phase': phase, 'host': covalicHost, 'log': minimalLog }) mail_utils.sendEmail(to=user['email'], subject='Submission processing error', text=html)
def ingestDataset(self, zipFile, user, name, owner, description, license, signature, anonymous, attribution, sendMail=False): """ Ingest an uploaded dataset from a .zip file of images. The images are extracted to a "Pre-review" folder within a new dataset folder. """ Folder = self.model('folder') # Create dataset folder dataset = self.createDataset(name, description, user) # Set dataset metadata, including license info dataset = self.setMetadata(dataset, { 'owner': owner, 'signature': signature, 'anonymous': anonymous, 'attribution': attribution, 'license': license, 'metadataFiles': [] }) prereviewFolder = Folder.createFolder( parent=dataset, name='Pre-review', parentType='folder', creator=user, public=False) prereviewFolder = Folder.copyAccessPolicies( dataset, prereviewFolder, save=True) # Process zip file # TODO: gracefully clean up after exceptions in handleZip self._handleZip(prereviewFolder, user, zipFile) # Send email confirmations if sendMail: host = mail_utils.getEmailUrlPrefix() params = { 'group': False, 'host': host, # We intentionally leak full user details here, even though all # email recipients may not have access permissions to the user 'user': user, 'name': name, 'owner': owner, 'description': description, 'license': license, 'signature': signature, 'attribution': 'Anonymous' if anonymous else attribution } subject = 'ISIC Archive: Dataset Upload Confirmation' templateFilename = 'ingestDatasetConfirmation.mako' # Mail user html = mail_utils.renderTemplate(templateFilename, params) mail_utils.sendEmail(to=user['email'], subject=subject, text=html) # Mail 'Dataset QC Reviewers' group params['group'] = True isic_mail_utils.sendEmailToGroup( groupName='Dataset QC Reviewers', templateFilename=templateFilename, templateParams=params, subject=subject) return dataset