def notify_added_contributor(node, contributor, auth=None, throttle=None): throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE # Exclude forks and templates because the user forking/templating the project gets added # via 'add_contributor' but does not need to get notified. # Only email users for projects, or for components where they are not contributors on the parent node. if (contributor.is_registered and not node.template_node and not node.is_fork and (not node.parent_node or (node.parent_node and not node.parent_node.is_contributor(contributor)))): contributor_record = contributor.contributor_added_email_records.get(node._id, {}) if contributor_record: timestamp = contributor_record.get('last_sent', None) if timestamp: if not throttle_period_expired(timestamp, throttle): return else: contributor.contributor_added_email_records[node._id] = {} mails.send_mail( contributor.username, mails.CONTRIBUTOR_ADDED, user=contributor, node=node, referrer_name=auth.user.fullname if auth else '', all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor) ) contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp() contributor.save()
def request_export(auth): mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.REQUEST_EXPORT, user=auth.user, ) return {'message': 'Sent account export request'}
def email_transactional(subscribed_user_ids, uid, event, **context): """ :param subscribed_user_ids: mod-odm User object ids :param uid: id of the event owner (Node or User) :param event: name of notification event (e.g. 'comments') :param context: context variables for email template :return: """ template = event + '.html.mako' subject = Template(EMAIL_SUBJECT_MAP[event]).render(**context) for user_id in subscribed_user_ids: user = website_models.User.load(user_id) email = user.username context['localized_timestamp'] = localize_timestamp(context.get('timestamp'), user) message = mails.render_message(template, **context) if context.get('commenter')._id != user._id: mails.send_mail( to_addr=email, mail=mails.TRANSACTIONAL, mimetype='html', name=user.fullname, node_id=context.get('node_id'), node_title=context.get('title'), subject=subject, message=message, url=get_settings_url(uid, user) )
def send_confirm_email(user, email): """Sends a confirmation email to `user` to a given email. :raises: KeyError if user does not have a confirmation token for the given email. """ confirmation_url = user.get_confirmation_url( email, external=True, force=True, ) try: merge_target = User.find_one(Q('emails', 'eq', email)) except NoResultsFound: merge_target = None mails.send_mail( email, mails.CONFIRM_MERGE if merge_target else mails.CONFIRM_EMAIL, 'plain', user=user, confirmation_url=confirmation_url, email=email, merge_target=merge_target, )
def _send_approval_request_email(self, user, template, context): mails.send_mail( user.username, template, user=user, **context )
def request_deactivation(auth): mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.REQUEST_DEACTIVATION, user=auth.user, ) return {'message': 'Sent account deactivation request'}
def main(dry=True): qs = PreprintService.objects.filter( is_published=True, node__is_deleted=False ).select_related('node').prefetch_related('node___contributors').order_by('pk') count = qs.count() pbar = progressbar.ProgressBar(maxval=count).start() contributors_emailed = set() logger.info('Sending emails to users for {} published preprints...'.format(count)) paginator = Paginator(qs, PAGE_SIZE) n_processed = 0 for page_num in paginator.page_range: page = paginator.page(page_num) for preprint in page.object_list: users = preprint.node.contributors.filter(is_active=True) for user in users: if user._id not in contributors_emailed: if not dry: mails.send_mail( mail=mails.PREPRINT_DOI_CHANGE, to_addr=user.username, can_change_preferences=False, user=user ) contributors_emailed.add(user._id) n_processed += len(page.object_list) pbar.update(n_processed) logger.info('Sent email to {} users from {} preprints'.format(len(contributors_emailed), count))
def send_claim_registered_email(claimer, unreg_user, node, throttle=24 * 3600): unclaimed_record = unreg_user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unreg_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) timestamp = unclaimed_record.get('last_sent') if throttle_period_expired(timestamp, throttle): # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGiSTERED, user=unreg_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unreg_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def forgot_password(): form = ForgotPasswordForm(request.form, prefix='forgot_password') if form.validate(): email = form.email.data user_obj = get_user(username=email) if user_obj: user_obj.verification_key = security.random_string(20) user_obj.save() reset_link = "http://{0}{1}".format( request.host, web_url_for( 'reset_password', verification_key=user_obj.verification_key ) ) mails.send_mail( to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link ) status.push_status_message('Reset email sent to {0}'.format(email)) else: status.push_status_message('Email {email} not found'.format(email=email)) forms.push_errors_to_status(form.errors) return auth_login(forgot_password_form=form)
def send_users_email(send_type): """Find pending Emails and amalgamates them into a single Email. :param send_type :return: """ grouped_emails = get_users_emails(send_type) for group in grouped_emails: user = OSFUser.load(group['user_id']) if not user: log_exception() continue info = group['info'] notification_ids = [message['_id'] for message in info] sorted_messages = group_by_node(info) if sorted_messages: if not user.is_disabled: mails.send_mail( to_addr=user.username, mimetype='html', mail=mails.DIGEST, name=user.fullname, message=sorted_messages, ) remove_notifications(email_notification_ids=notification_ids)
def _send_global_and_node_emails(send_type): """ Called by `send_users_email`. Send all global and node-related notification emails. """ grouped_emails = get_users_emails(send_type) for group in grouped_emails: user = OSFUser.load(group['user_id']) if not user: log_exception() continue info = group['info'] notification_ids = [message['_id'] for message in info] sorted_messages = group_by_node(info) if sorted_messages: if not user.is_disabled: # If there's only one node in digest we can show it's preferences link in the template. notification_nodes = sorted_messages['children'].keys() node = AbstractNode.load(notification_nodes[0]) if len( notification_nodes) == 1 else None mails.send_mail( to_addr=user.username, mimetype='html', can_change_node_preferences=bool(node), node=node, mail=mails.DIGEST, name=user.fullname, message=sorted_messages, ) remove_notifications(email_notification_ids=notification_ids)
def _send_preprint_confirmation(self, auth): # Send creator confirmation email recipient = self.creator event_type = utils.find_subscription_type('global_reviews') user_subscriptions = get_user_subscriptions(recipient, event_type) if self.provider._id == 'osf': logo = settings.OSF_PREPRINTS_LOGO else: logo = self.provider._id context = { 'domain': settings.DOMAIN, 'reviewable': self, 'workflow': self.provider.reviews_workflow, 'provider_url': '{domain}preprints/{provider_id}'.format( domain=self.provider.domain or settings.DOMAIN, provider_id=self.provider._id if not self.provider.domain else '').strip('/'), 'provider_contact_email': self.provider.email_contact or settings.OSF_CONTACT_EMAIL, 'provider_support_email': self.provider.email_support or settings.OSF_SUPPORT_EMAIL, 'no_future_emails': user_subscriptions['none'], 'is_creator': True, 'provider_name': 'OSF Preprints' if self.provider.name == 'Open Science Framework' else self.provider.name, 'logo': logo, } mails.send_mail( recipient.username, mails.REVIEWS_SUBMISSION_CONFIRMATION, mimetype='html', user=recipient, **context )
def send_confirm_email(user, email): """Sends a confirmation email to `user` to a given email. :raises: KeyError if user does not have a confirmation token for the given email. """ confirmation_url = user.get_confirmation_url( email, external=True, force=True, ) try: merge_target = User.find_one(Q('emails', 'eq', email)) except NoResultsFound: merge_target = None campaign = campaigns.campaign_for_user(user) # Choose the appropriate email template to use if merge_target: mail_template = mails.CONFIRM_MERGE elif campaign: mail_template = campaigns.email_template_for_campaign(campaign) else: mail_template = mails.CONFIRM_EMAIL mails.send_mail( email, mail_template, 'plain', user=user, confirmation_url=confirmation_url, email=email, merge_target=merge_target, )
def forgot_password_post(): """Attempt to send user password reset or return respective error. """ form = ForgotPasswordForm(request.form, prefix='forgot_password') if form.validate(): email = form.email.data user_obj = get_user(email=email) if user_obj: user_obj.verification_key = security.random_string(20) user_obj.save() reset_link = "http://{0}{1}".format( request.host, web_url_for( 'reset_password', verification_key=user_obj.verification_key ) ) mails.send_mail( to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link ) status.push_status_message( ('An email with instructions on how to reset the password ' 'for the account associated with {0} has been sent. If you ' 'do not receive an email and believe you should have please ' 'contact OSF Support.').format(email), 'success') forms.push_errors_to_status(form.errors) return auth_login(forgot_password_form=form)
def send_digest(grouped_digests): """ Send digest emails and remove digests for sent messages in a callback. :param grouped_digests: digest notification messages from the past 24 hours grouped by user :return: """ for group in grouped_digests: user = User.load(group['user_id']) if not user: sentry.log_exception() sentry.log_message("A user with this username does not exist.") return info = group['info'] digest_notification_ids = [message['_id'] for message in info] sorted_messages = group_messages_by_node(info) if sorted_messages: logger.info('Sending email digest to user {0!r}'.format(user)) mails.send_mail( to_addr=user.username, mimetype='html', mail=mails.DIGEST, name=user.fullname, message=sorted_messages, callback=remove_sent_digest_notifications.si( digest_notification_ids=digest_notification_ids ) )
def meeting_hook(): """View function for email conference submission. """ message = ConferenceMessage() try: message.verify() except ConferenceError as error: logger.error(error) raise HTTPError(httplib.NOT_ACCEPTABLE) try: conference = Conference.get_by_endpoint(message.conference_name, active=False) except ConferenceError as error: logger.error(error) raise HTTPError(httplib.NOT_ACCEPTABLE) if not conference.active: send_mail( message.sender_email, CONFERENCE_INACTIVE, fullname=message.sender_display, presentations_url=web_url_for('conference_view', _absolute=True), can_change_preferences=False, logo=settings.OSF_MEETINGS_LOGO, ) raise HTTPError(httplib.NOT_ACCEPTABLE) add_poster_by_email(conference=conference, message=message)
def send_users_email(send_type): """Find pending Emails and amalgamates them into a single Email. :param send_type :return: """ grouped_emails = get_users_emails(send_type) if not grouped_emails: return for group in grouped_emails: user = User.load(group["user_id"]) if not user: log_exception() continue info = group["info"] notification_ids = [message["_id"] for message in info] sorted_messages = group_by_node(info) if sorted_messages: mails.send_mail( to_addr=user.username, mimetype="html", mail=mails.DIGEST, name=user.fullname, message=sorted_messages, callback=remove_notifications(email_notification_ids=notification_ids), )
def main(send_email=False): logger.info('Starting Project storage audit') init_app(set_backends=True, routes=False) lines = [] projects = {} users = defaultdict(lambda: (0, 0)) for node in Node.find(Q('__backrefs.parent.node.nodes', 'eq', None)): # ODM hack to ignore all nodes with parents if node._id in WHITE_LIST: continue # Dont count whitelisted nodes against users projects[node] = get_usage(node) for contrib in node.contributors: if node.can_edit(user=contrib): users[contrib] = tuple(map(sum, zip(users[contrib], projects[node]))) # Adds tuples together, map(sum, zip((a, b), (c, d))) -> (a+c, b+d) for collection, limit in ((users, USER_LIMIT), (projects, PROJECT_LIMIT)): for item, (used, deleted) in filter(functools.partial(limit_filter, limit), collection.items()): line = '{!r} has exceeded the limit {:.2f}GBs ({}b) with {:.2f}GBs ({}b) used and {:.2f}GBs ({}b) deleted.'.format(item, limit / GBs, limit, used / GBs, used, deleted / GBs, deleted) logger.info(line) lines.append(line) if lines: if send_email: logger.info('Sending email...') mails.send_mail('*****@*****.**', mails.EMPTY, body='\n'.join(lines), subject='Script: OsfStorage usage audit') else: logger.info('send_email is False, not sending email'.format(len(lines))) logger.info('{} offending project(s) and user(s) found'.format(len(lines))) else: logger.info('No offending projects or users found')
def email_transactional(recipient_ids, uid, event, user, node, timestamp, **context): """ :param recipient_ids: mod-odm User object ids :param uid: id of the event owner (Node or User) :param event: name of notification event (e.g. 'comments') :param context: context variables for email template See notify for specifics :return: """ template = event + '.html.mako' context['title'] = node.title context['user'] = user subject = Template(EMAIL_SUBJECT_MAP[event]).render(**context) for user_id in recipient_ids: recipient = website_models.User.load(user_id) email = recipient.username context['localized_timestamp'] = localize_timestamp(timestamp, recipient) message = mails.render_message(template, **context) if user._id != recipient._id: mails.send_mail( to_addr=email, mail=mails.TRANSACTIONAL, mimetype='html', name=recipient.fullname, node_id=node._id, node_title=node.title, subject=subject, message=message, url=get_settings_url(uid, recipient) )
def send_claim_email(email, user, node, notify=True, throttle=24 * 3600): """Send an email for claiming a user account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. """ claimer_email = email.lower().strip() unclaimed_record = user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = user.get_claim_url(node._primary_key, external=True) # If given email is the same provided by user, just send to that email if unclaimed_record.get('email') == claimer_email: mail_tpl = mails.INVITE to_addr = claimer_email unclaimed_record['claimer_email'] = claimer_email user.save() else: # Otherwise have the referrer forward the email to the user # roll the valid token for each email, thus user cannot change email and approve a different email address timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(400, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) unclaimed_record['last_sent'] = get_timestamp() unclaimed_record['token'] = generate_confirm_token() unclaimed_record['claimer_email'] = claimer_email user.save() claim_url = user.get_claim_url(node._primary_key, external=True) if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail( claimer_email, pending_mail, user=user, referrer=referrer, fullname=unclaimed_record['name'], node=node ) mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username mails.send_mail( to_addr, mail_tpl, user=user, referrer=referrer, node=node, claim_url=claim_url, email=claimer_email, fullname=unclaimed_record['name'] ) return to_addr
def confirm_email_get(token, auth=None, **kwargs): """View for email confirmation links. Authenticates and redirects to user settings page if confirmation is successful, otherwise shows an "Expired Link" error. methods: GET """ user = User.load(kwargs['uid']) is_merge = 'confirm_merge' in request.args is_initial_confirmation = not user.date_confirmed if user is None: raise HTTPError(http.NOT_FOUND) if auth and auth.user and (auth.user._id == user._id or auth.user._id == user.merged_by._id): if not is_merge: # determine if the user registered through a campaign campaign = campaigns.campaign_for_user(user) if campaign: return redirect( campaigns.campaign_url_for(campaign) ) status.push_status_message(language.WELCOME_MESSAGE, 'default', jumbotron=True) # Go to dashboard return redirect(web_url_for('dashboard')) status.push_status_message(language.MERGE_COMPLETE, 'success') return redirect(web_url_for('user_account')) try: user.confirm_email(token, merge=is_merge) except exceptions.EmailConfirmTokenError as e: raise HTTPError(http.BAD_REQUEST, data={ 'message_short': e.message_short, 'message_long': e.message_long }) if is_initial_confirmation: user.date_last_login = datetime.datetime.utcnow() user.save() # Send out our welcome message mails.send_mail( to_addr=user.username, mail=mails.WELCOME, mimetype='html', user=user ) # Redirect to CAS and authenticate the user with a verification key. user.verification_key = security.random_string(20) user.save() return redirect(cas.get_login_url( request.url, auto=True, username=user.username, verification_key=user.verification_key ))
def send_claim_registered_email(claimer, unclaimed_user, node, throttle=24 * 3600): """ A registered user claiming the unclaimed user account as an contributor to a project. Send an email for claiming the account to the referrer and notify the claimer. :param claimer: the claimer :param unclaimed_user: the user account to claim :param node: the project node where the user account is claimed :param throttle: the time period in seconds before another claim for the account can be made :return: :raise: http.BAD_REQUEST """ unclaimed_record = unclaimed_user.get_unclaimed_record(node._primary_key) # check throttle timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError(http.BAD_REQUEST, data=dict( message_long='User account can only be claimed with an existing user once every 24 hours' )) # roll the valid token for each email, thus user cannot change email and approve a different email address verification_key = generate_verification_key(verification_type='claim') unclaimed_record['token'] = verification_key['token'] unclaimed_record['expires'] = verification_key['expires'] unclaimed_record['claimer_email'] = claimer.username unclaimed_user.save() referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unclaimed_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGISTERED, user=unclaimed_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unclaimed_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def send_confirm_email(user, email, renew=False, external_id_provider=None, external_id=None, destination=None): """ Sends `user` a confirmation to the given `email`. :param user: the user :param email: the email :param renew: refresh the token :param external_id_provider: user's external id provider :param external_id: user's external id :param destination: the destination page to redirect after confirmation :return: :raises: KeyError if user does not have a confirmation token for the given email. """ confirmation_url = user.get_confirmation_url( email, external=True, force=True, renew=renew, external_id_provider=external_id_provider, destination=destination ) try: merge_target = User.find_one(Q('emails', 'eq', email)) except NoResultsFound: merge_target = None campaign = campaigns.campaign_for_user(user) # Choose the appropriate email template to use and add existing_user flag if a merge or adding an email. if external_id_provider and external_id: # first time login through external identity provider if user.external_identity[external_id_provider][external_id] == 'CREATE': mail_template = mails.EXTERNAL_LOGIN_CONFIRM_EMAIL_CREATE elif user.external_identity[external_id_provider][external_id] == 'LINK': mail_template = mails.EXTERNAL_LOGIN_CONFIRM_EMAIL_LINK elif merge_target: # merge account mail_template = mails.CONFIRM_MERGE confirmation_url = '{}?logout=1'.format(confirmation_url) elif user.is_active: # add email mail_template = mails.CONFIRM_EMAIL confirmation_url = '{}?logout=1'.format(confirmation_url) elif campaign: # campaign # TODO: In the future, we may want to make confirmation email configurable as well (send new user to # appropriate landing page or with redirect after) mail_template = campaigns.email_template_for_campaign(campaign) else: # account creation mail_template = mails.INITIAL_CONFIRM_EMAIL mails.send_mail( email, mail_template, 'plain', user=user, confirmation_url=confirmation_url, email=email, merge_target=merge_target, external_id_provider=external_id_provider, )
def notify_added_contributor(node, contributor, auth=None, throttle=None, email_template='default'): if email_template == 'false': return throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE # Email users for projects, or for components where they are not contributors on the parent node. if contributor.is_registered and \ (not node.parent_node or (node.parent_node and not node.parent_node.is_contributor(contributor))): mimetype = 'html' preprint_provider = None logo = None if email_template == 'preprint': email_template, preprint_provider = find_preprint_provider(node) if not email_template or not preprint_provider: return email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT')(email_template, preprint_provider) if preprint_provider._id == 'osf': logo = settings.OSF_PREPRINTS_LOGO else: logo = preprint_provider._id elif email_template == 'access_request': mimetype = 'html' email_template = getattr(mails, 'CONTRIBUTOR_ADDED_ACCESS_REQUEST'.format(email_template.upper())) elif node.is_preprint: email_template = getattr(mails, 'CONTRIBUTOR_ADDED_PREPRINT_NODE_FROM_OSF'.format(email_template.upper())) logo = settings.OSF_PREPRINTS_LOGO else: email_template = getattr(mails, 'CONTRIBUTOR_ADDED_DEFAULT'.format(email_template.upper())) contributor_record = contributor.contributor_added_email_records.get(node._id, {}) if contributor_record: timestamp = contributor_record.get('last_sent', None) if timestamp: if not throttle_period_expired(timestamp, throttle): return else: contributor.contributor_added_email_records[node._id] = {} mails.send_mail( contributor.username, email_template, mimetype=mimetype, user=contributor, node=node, referrer_name=auth.user.fullname if auth else '', all_global_subscriptions_none=check_if_all_global_subscriptions_are_none(contributor), branded_service=preprint_provider, can_change_preferences=False, logo=logo if logo else settings.OSF_LOGO, osf_contact_email=settings.OSF_CONTACT_EMAIL ) contributor.contributor_added_email_records[node._id]['last_sent'] = get_timestamp() contributor.save() elif not contributor.is_registered: unreg_contributor_added.send(node, contributor=contributor, auth=auth, email_template=email_template)
def authenticate(self, request): try: payload = jwt.decode( jwe.decrypt(request.body, settings.JWE_SECRET), settings.JWT_SECRET, options={'verify_exp': False}, algorithm='HS256' ) except (jwt.InvalidTokenError, TypeError): raise AuthenticationFailed # The JWT `data` payload is expected in the following structure. # # {"provider": { # "idp": "https://login.circle.edu/idp/shibboleth", # "id": "CIR", # "user": { # "middleNames": "", # "familyName": "", # "givenName": "", # "fullname": "Circle User", # "suffix": "", # "username": "******" # } # }} data = json.loads(payload['data']) provider = data['provider'] institution = Institution.load(provider['id']) if not institution: raise AuthenticationFailed('Invalid institution id specified "{}"'.format(provider['id'])) username = provider['user']['username'] fullname = provider['user']['fullname'] user, created = get_or_create_user(fullname, username, reset_password=False) if created: user.given_name = provider['user'].get('givenName') user.middle_names = provider['user'].get('middleNames') user.family_name = provider['user'].get('familyName') user.suffix = provider['user'].get('suffix') user.date_last_login = datetime.utcnow() user.save() # User must be saved in order to have a valid _id user.register(username) send_mail( to_addr=user.username, mail=WELCOME_OSF4I, mimetype='html', user=user ) if institution not in user.affiliated_institutions: user.affiliated_institutions.append(institution) user.save() return user, None
def _send_rejection_email(self, user, draft): schema = draft.registration_schema prereg_schema = prereg_utils.get_prereg_schema() if schema._id == prereg_schema._id: mails.send_mail(user.username, mails.PREREG_CHALLENGE_REJECTED, user=user, draft_url=draft.absolute_url) else: raise NotImplementedError("TODO: add a generic email template for registration approvals")
def send_desk_share_error(node, resp, retries): mails.send_mail( to_addr=settings.OSF_SUPPORT_EMAIL, mail=mails.SHARE_ERROR_DESK, node=node, resp=resp, retries=retries, )
def send_retraction_and_embargo_addition_message(contrib, label, mail, dry_run=True): if label in contrib.security_messages: return logger.info('Sending message to user {0!r}'.format(contrib)) if not dry_run: send_mail(contrib.username, mail, user=contrib) contrib.security_messages[MESSAGE_NAME] = datetime.datetime.utcnow() contrib.save()
def send_desk_share_preprint_error(preprint, resp, retries): mails.send_mail( to_addr=settings.OSF_SUPPORT_EMAIL, mail=mails.SHARE_PREPRINT_ERROR_DESK, preprint=preprint, resp=resp, retries=retries, )
def forgot_password_post(): """ View for user to submit forgot password form. HTTP Method: POST :return {} """ form = ForgotPasswordForm(request.form, prefix='forgot_password') if not form.validate(): # Don't go anywhere forms.push_errors_to_status(form.errors) else: email = form.email.data status_message = ('If there is an OSF account associated with {0}, an email with instructions on how to ' 'reset the OSF password has been sent to {0}. If you do not receive an email and believe ' 'you should have, please contact OSF Support. ').format(email) kind = 'success' # check if the user exists user_obj = get_user(email=email) if user_obj: # rate limit forgot_password_post if not throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE): status_message = 'You have recently requested to change your password. Please wait a few minutes ' \ 'before trying again.' kind = 'error' else: # TODO [OSF-6673]: Use the feature in [OSF-6998] for user to resend claim email. # if the user account is not claimed yet if (user_obj.is_invited and user_obj.unclaimed_records and not user_obj.date_last_login and not user_obj.is_claimed and not user_obj.is_registered): status_message = 'You cannot reset password on this account. Please contact OSF Support.' kind = 'error' else: # new random verification key (v2) user_obj.verification_key_v2 = generate_verification_key(verification_type='password') user_obj.email_last_sent = datetime.datetime.utcnow() user_obj.save() reset_link = furl.urljoin( settings.DOMAIN, web_url_for( 'reset_password_get', uid=user_obj._id, token=user_obj.verification_key_v2['token'] ) ) mails.send_mail( to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link ) status.push_status_message(status_message, kind=kind, trust=False) return {}
def send_claim_email(email, user, node, notify=True, throttle=24 * 3600): """Send an email for claiming a user account. Either sends to the given email or the referrer's email, depending on the email address provided. :param str email: The address given in the claim user form :param User user: The User record to claim. :param Node node: The node where the user claimed their account. :param bool notify: If True and an email is sent to the referrer, an email will also be sent to the invited user about their pending verification. :param int throttle: Time period (in seconds) after the referrer is emailed during which the referrer will not be emailed again. """ invited_email = email.lower().strip() unclaimed_record = user.get_unclaimed_record(node._primary_key) referrer = User.load(unclaimed_record['referrer_id']) claim_url = user.get_claim_url(node._primary_key, external=True) # If given email is the same provided by user, just send to that email if unclaimed_record.get('email', None) == invited_email: mail_tpl = mails.INVITE to_addr = invited_email else: # Otherwise have the referrer forward the email to the user if notify: pending_mail = mails.PENDING_VERIFICATION mails.send_mail(invited_email, pending_mail, user=user, referrer=referrer, fullname=unclaimed_record['name'], node=node) timestamp = unclaimed_record.get('last_sent') if throttle_period_expired(timestamp, throttle): unclaimed_record['last_sent'] = get_timestamp() user.save() else: # Don't send the email to the referrer return mail_tpl = mails.FORWARD_INVITE to_addr = referrer.username mails.send_mail(to_addr, mail_tpl, user=user, referrer=referrer, node=node, claim_url=claim_url, email=invited_email, fullname=unclaimed_record['name']) return to_addr
def send_archiver_size_exceeded_mails(src, user, stat_result): mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.ARCHIVE_SIZE_EXCEEDED_DESK, user=user, src=src, stat_result=stat_result ) mails.send_mail( to_addr=user.username, mail=mails.ARCHIVE_SIZE_EXCEEDED_USER, user=user, src=src, can_change_preferences=False, mimetype='html', )
def send_archiver_uncaught_error_mails(src, user, results): mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.ARCHIVE_UNCAUGHT_ERROR_DESK, user=user, src=src, results=results, ) mails.send_mail( to_addr=user.username, mail=mails.ARCHIVE_UNCAUGHT_ERROR_USER, user=user, src=src, results=results, mimetype='html', )
def request_export(auth): user = auth.user if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'Too many requests. Please wait a while before sending another account export request.', 'error_type': 'throttle_error'}) mails.send_mail( to_addr=settings.OSF_SUPPORT_EMAIL, mail=mails.REQUEST_EXPORT, user=auth.user, can_change_preferences=False, ) user.email_last_sent = timezone.now() user.save() return {'message': 'Sent account export request'}
def _send_preprint_confirmation(self, auth): # Send creator confirmation email if self.provider._id == 'osf': email_template = getattr(mails, 'PREPRINT_CONFIRMATION_DEFAULT') else: email_template = getattr(mails, 'PREPRINT_CONFIRMATION_BRANDED')( self.provider) mails.send_mail( auth.user.username, email_template, user=auth.user, node=self.node, preprint=self, osf_contact_email=settings.OSF_CONTACT_EMAIL, )
def forgot_password_post(): """ View for user to submit forgot password form. HTTP Method: POST :return {} """ form = ForgotPasswordForm(request.form, prefix='forgot_password') if not form.validate(): # Don't go anywhere forms.push_errors_to_status(form.errors) else: email = form.email.data status_message = ( 'If there is an OSF account associated with {0}, an email with instructions on how to ' 'reset the OSF password has been sent to {0}. If you do not receive an email and believe ' 'you should have, please contact OSF Support. ').format(email) kind = 'success' # check if the user exists user_obj = get_user(email=email) if user_obj: # rate limit forgot_password_post if not throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE): status_message = 'You have recently requested to change your password. Please wait a few minutes ' \ 'before trying again.' kind = 'error' # TODO [OSF-6673]: Use the feature in [OSF-6998] for user to resend claim email. elif user_obj.is_active: # new random verification key (v2) user_obj.verification_key_v2 = generate_verification_key( verification_type='password') user_obj.email_last_sent = timezone.now() user_obj.save() reset_link = furl.urljoin( settings.DOMAIN, web_url_for('reset_password_get', uid=user_obj._id, token=user_obj.verification_key_v2['token'])) mails.send_mail(to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link) status.push_status_message(status_message, kind=kind, trust=False) return {}
def send_archiver_file_not_found_mails(src, user, results): mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.ARCHIVE_FILE_NOT_FOUND_DESK, user=user, src=src, results=results, ) mails.send_mail( to_addr=user.username, mail=mails.ARCHIVE_FILE_NOT_FOUND_USER, user=user, src=src, results=results, can_change_preferences=False, mimetype='html', )
def send_security_message(user, label, mail): if label in user.security_messages: return # Pass mailer so that celery is not used # Email synchronously so that user is only saved after email has been sent mails.send_mail( user.username, mail, from_addr=FROM_ADDR, mailer=send_email, user=user, username=settings.MANDRILL_USERNAME, password=settings.MANDRILL_PASSWORD, mail_server=settings.MANDRILL_MAIL_SERVER, ) user.security_messages[label] = timezone.now() user.save()
def _send_rejection_email(self, user, draft): schema = draft.registration_schema prereg_schema = prereg_utils.get_prereg_schema() if schema._id == prereg_schema._id: mails.send_mail( user.username, mails.PREREG_CHALLENGE_REJECTED, user=user, draft_url=draft.absolute_url, can_change_preferences=False, logo=osf_settings.OSF_PREREG_LOGO ) else: raise NotImplementedError( 'TODO: add a generic email template for registration approvals' )
def send_archiver_copy_error_mails(src, user, results): mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.ARCHIVE_COPY_ERROR_DESK, user=user, src=src, results=results, ) mails.send_mail( to_addr=user.username, mail=mails.ARCHIVE_COPY_ERROR_USER, user=user, src=src, results=results, can_change_preferences=False, mimetype='html', )
def notify_added_contributor(node, contributor, auth=None, throttle=None, email_template='default'): throttle = throttle or settings.CONTRIBUTOR_ADDED_EMAIL_THROTTLE # Exclude forks and templates because the user forking/templating the project gets added # via 'add_contributor' but does not need to get notified. # Only email users for projects, or for components where they are not contributors on the parent node. if (contributor.is_registered and not node.template_node and not node.is_fork and (not node.parent_node or (node.parent_node and not node.parent_node.is_contributor(contributor)))): email_template = getattr( mails, 'CONTRIBUTOR_ADDED_{}'.format(email_template.upper())) contributor_record = contributor.contributor_added_email_records.get( node._id, {}) if contributor_record: timestamp = contributor_record.get('last_sent', None) if timestamp: if not throttle_period_expired(timestamp, throttle): return else: contributor.contributor_added_email_records[node._id] = {} mails.send_mail( contributor.username, email_template, user=contributor, node=node, referrer_name=auth.user.fullname if auth else '', all_global_subscriptions_none= check_if_all_global_subscriptions_are_none(contributor)) contributor.contributor_added_email_records[ node._id]['last_sent'] = get_timestamp() contributor.save() elif not contributor.is_registered: unreg_contributor_added.send(node, contributor=contributor, auth=auth, email_template=email_template)
def request_deactivation(auth): user = auth.user if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError(http.BAD_REQUEST, data={ 'message_long': 'Too many requests. Please wait a while before sending another account deactivation request.', 'error_type': 'throttle_error' }) mails.send_mail( to_addr=settings.OSF_SUPPORT_EMAIL, mail=mails.REQUEST_DEACTIVATION, user=auth.user, ) user.email_last_sent = timezone.now() user.requested_deactivation = True user.save() return {'message': 'Sent account deactivation request'}
def send_archiver_uncaught_error_mails(src, user, results, url): mails.send_mail( to_addr=settings.OSF_SUPPORT_EMAIL, mail=mails.ARCHIVE_UNCAUGHT_ERROR_DESK, user=user, src=src, results=results, url=url, ) mails.send_mail( to_addr=user.username, mail=mails.ARCHIVE_UNCAUGHT_ERROR_USER, user=user, src=src, results=results, can_change_preferences=False, mimetype='html', )
def main(): prereg_csv = generate_prereg_csv() filename = 'prereg_{}.csv.gz'.format(timezone.now().isoformat()) output = io.BytesIO() with gzip.GzipFile(filename=filename, mode='wb', fileobj=output) as gzip_obj: gzip_obj.write(prereg_csv.getvalue()) mails.send_mail( mail=mails.PREREG_CSV, to_addr=settings.SUPPORT_EMAIL, attachment_name=filename, attachment_content=output.getvalue(), ) logger.info('Updated prereg CSV email sent.')
def _send_reviews_moderator_emails(send_type): """ Called by `send_users_email`. Send all reviews triggered emails. """ grouped_emails = get_moderators_emails(send_type) for group in grouped_emails: user = OSFUser.load(group['user_id']) info = group['info'] notification_ids = [message['_id'] for message in info] provider = AbstractProvider.objects.get(id=group['provider_id']) additional_context = dict() if isinstance(provider, RegistrationProvider): provider_type = 'registration' submissions_url = f'{settings.DOMAIN}registries/{provider._id}/moderation/submissions', withdrawals_url = f'{submissions_url}?state=pending_withdraw' notification_settings_url = f'{settings.DOMAIN}registries/{provider._id}/moderation/notifications' if provider.brand: additional_context = { 'logo_url': provider.brand.hero_logo_image, 'top_bar_color': provider.brand.primary_color } else: provider_type = 'preprint' submissions_url = f'{settings.DOMAIN}reviews/preprints/{provider._id}', withdrawals_url = '' notification_settings_url = f'{settings.DOMAIN}reviews/{provider_type}s/{provider._id}/notifications' if not user.is_disabled: mails.send_mail( to_addr=user.username, mimetype='html', mail=mails.DIGEST_REVIEWS_MODERATORS, name=user.fullname, message=info, provider_name=provider.name, reviews_submissions_url=submissions_url, notification_settings_url=notification_settings_url, reviews_withdrawal_url=withdrawals_url, is_reviews_moderator_notification=True, is_admin=provider.get_group(ADMIN).user_set.filter( id=user.id).exists(), provider_type=provider_type, **additional_context) remove_notifications(email_notification_ids=notification_ids)
def main(send_email=False): logger.info('Starting Project storage audit') init_app(set_backends=True, routes=False) lines = [] projects = {} users = defaultdict(lambda: (0, 0)) progress_bar = progressbar.ProgressBar(maxval=Node.find(Q('parent_node', 'eq', None)).count()).start() for i, node in enumerate(Node.find(Q('parent_node', 'eq', None))): progress_bar.update(i+1) if node._id in WHITE_LIST: continue # Dont count whitelisted nodes against users projects[node._id] = get_usage(node) for contrib in node.contributors: if node.can_edit(user=contrib): users[contrib._id] = tuple(map(sum, zip(users[contrib._id], projects[node._id]))) # Adds tuples together, map(sum, zip((a, b), (c, d))) -> (a+c, b+d) if i % 25 == 0: # Clear all caches for key in ('node', 'user', 'fileversion', 'storedfilenode'): Node._cache.data.get(key, {}).clear() Node._object_cache.data.get(key, {}).clear() # Collect garbage gc.collect() progress_bar.finish() for model, collection, limit in ((User, users, USER_LIMIT), (Node, projects, PROJECT_LIMIT)): for item, (used, deleted) in filter(functools.partial(limit_filter, limit), collection.items()): line = '{!r} has exceeded the limit {:.2f}GBs ({}b) with {:.2f}GBs ({}b) used and {:.2f}GBs ({}b) deleted.'.format(model.load(item), limit / GBs, limit, used / GBs, used, deleted / GBs, deleted) logger.info(line) lines.append(line) if lines: if send_email: logger.info('Sending email...') mails.send_mail('*****@*****.**', mails.EMPTY, body='\n'.join(lines), subject='Script: OsfStorage usage audit') else: logger.info('send_email is False, not sending email'.format(len(lines))) logger.info('{} offending project(s) and user(s) found'.format(len(lines))) else: logger.info('No offending projects or users found')
def _send_preprint_confirmation(self, auth): # Send creator confirmation email recipient = self.node.creator event_type = utils.find_subscription_type('global_reviews') user_subscriptions = get_user_subscriptions(recipient, event_type) if self.provider._id == 'osf': logo = settings.OSF_PREPRINTS_LOGO else: logo = self.provider._id context = { 'domain': settings.DOMAIN, 'reviewable': self, 'workflow': self.provider.reviews_workflow, 'provider_url': '{domain}preprints/{provider_id}'.format( domain=self.provider.domain or settings.DOMAIN, provider_id=self.provider._id if not self.provider.domain else '').strip('/'), 'provider_contact_email': self.provider.email_contact or settings.OSF_CONTACT_EMAIL, 'provider_support_email': self.provider.email_support or settings.OSF_SUPPORT_EMAIL, 'no_future_emails': user_subscriptions['none'], 'is_creator': True, 'provider_name': 'OSF Preprints' if self.provider.name == 'Open Science Framework' else self.provider.name, 'logo': logo, } mails.send_mail(recipient.username, mails.REVIEWS_SUBMISSION_CONFIRMATION, mimetype='html', user=recipient, **context)
def send_claim_registered_email(claimer, unreg_user, node, throttle=24 * 3600): unclaimed_record = unreg_user.get_unclaimed_record(node._primary_key) # roll the valid token for each email, thus user cannot change email and approve a different email address timestamp = unclaimed_record.get('last_sent') if not throttle_period_expired(timestamp, throttle): raise HTTPError( 400, data=dict( message_long= 'User account can only be claimed with an existing user once every 24 hours' )) unclaimed_record['token'] = generate_confirm_token() unclaimed_record['claimer_email'] = claimer.username unreg_user.save() referrer = User.load(unclaimed_record['referrer_id']) claim_url = web_url_for( 'claim_user_registered', uid=unreg_user._primary_key, pid=node._primary_key, token=unclaimed_record['token'], _external=True, ) # Send mail to referrer, telling them to forward verification link to claimer mails.send_mail( referrer.username, mails.FORWARD_INVITE_REGiSTERED, user=unreg_user, referrer=referrer, node=node, claim_url=claim_url, fullname=unclaimed_record['name'], ) unclaimed_record['last_sent'] = get_timestamp() unreg_user.save() # Send mail to claimer, telling them to wait for referrer mails.send_mail( claimer.username, mails.PENDING_VERIFICATION_REGISTERED, fullname=claimer.fullname, referrer=referrer, node=node, )
def request_export(auth): user = auth.user if not throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): raise HTTPError( httplib.BAD_REQUEST, data={ 'message_long': 'Too many requests. Please wait a while before sending another account export request.', 'error_type': 'throttle_error' }) mails.send_mail( to_addr=settings.SUPPORT_EMAIL, mail=mails.REQUEST_EXPORT, user=auth.user, ) user.email_last_sent = datetime.datetime.utcnow() user.save() return {'message': 'Sent account export request'}
def inform_product_of_errors(initiator=None, provider=None, message=None): """Inform product owner of internal errors. """ email = settings.PRODUCT_OWNER_EMAIL_ADDRESS.get('Registration') if not email: logger.warning('Missing email for OSF Registration product owner.') return if not message: message = 'Bulk upload preparation failure' user = f'{initiator._id}, {initiator.fullname}, {initiator.username}' if initiator else 'UNIDENTIFIED' provider_name = provider.name if provider else 'UNIDENTIFIED' mails.send_mail( to_addr=email, mail=mails.REGISTRATION_BULK_UPLOAD_PRODUCT_OWNER, message=message, user=user, provider_name=provider_name, )
def report_stuck_dois(dry_run=True): preprints_with_pending_dois = Preprint.objects.filter(preprint_doi_created__isnull=True, is_published=True, date_published__lt=timezone.now() - time_since_published) if preprints_with_pending_dois: guids = ', '.join(preprints_with_pending_dois.values_list('guids___id', flat=True)) if not dry_run: mails.send_mail( to_addr=settings.OSF_SUPPORT_EMAIL, mail=mails.CROSSREF_DOIS_PENDING, pending_doi_count=preprints_with_pending_dois.count(), time_since_published=time_since_published.days, guids=guids, ) else: logger.info('DRY RUN') logger.info('There were {} stuck registrations for CrossRef, email sent to help desk'.format(preprints_with_pending_dois.count()))
def _send_retraction_email(node, user): """ Sends Approve/Disapprove email for retraction of a public registration to user :param node: Node being retracted :param user: Admin user to be emailed """ registration_link = node.web_url_for('view_project', _absolute=True) approval_time_span = settings.RETRACTION_PENDING_TIME.days * 24 initiators_fullname = node.retraction.initiated_by.fullname if node.has_permission(user, 'admin'): approval_token = node.retraction.approval_state[ user._id]['approval_token'] disapproval_token = node.retraction.approval_state[ user._id]['disapproval_token'] approval_link = node.web_url_for( 'node_registration_retraction_approve', token=approval_token, _absolute=True) disapproval_link = node.web_url_for( 'node_registration_retraction_disapprove', token=disapproval_token, _absolute=True) mails.send_mail( user.username, mails.PENDING_RETRACTION_ADMIN, 'plain', user=user, initiated_by=initiators_fullname, approval_link=approval_link, disapproval_link=disapproval_link, registration_link=registration_link, approval_time_span=approval_time_span, ) else: mails.send_mail(user.username, mails.PENDING_RETRACTION_NON_ADMIN, user=user, initiated_by=initiators_fullname, registration_link=registration_link)
def forgot_password_post(): """Attempt to send user password reset or return respective error. """ form = ForgotPasswordForm(request.form, prefix='forgot_password') if form.validate(): email = form.email.data status_message = ( 'If there is an OSF account associated with {0}, an email with instructions on how to reset ' 'the OSF password has been sent to {0}. If you do not receive an email and believe you ' 'should have, please contact OSF Support. ').format(email) user_obj = get_user(email=email) if user_obj: if throttle_period_expired(user_obj.email_last_sent, settings.SEND_EMAIL_THROTTLE): user_obj.verification_key = security.random_string(20) user_obj.email_last_sent = datetime.datetime.utcnow() user_obj.save() reset_link = furl.urljoin( settings.DOMAIN, web_url_for('reset_password', verification_key=user_obj.verification_key)) mails.send_mail(to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link) status.push_status_message(status_message, kind='success', trust=False) else: status.push_status_message( 'You have recently requested to change your password. Please wait a little ' 'while before trying again.', kind='error', trust=False) else: status.push_status_message(status_message, kind='success', trust=False) forms.push_errors_to_status(form.errors) return auth_login(forgot_password_form=form)
def main(send_email=False): logger.info('Starting Project storage audit') lines = [] projects = {} users = defaultdict(lambda: (0, 0)) top_level_nodes = AbstractNode.objects.get_roots() progress_bar = tqdm(total=top_level_nodes.count()) top_level_nodes = top_level_nodes.iterator() for i, node in enumerate(top_level_nodes): progress_bar.update(i+1) if node._id in WHITE_LIST: continue # Dont count whitelisted nodes against users projects[node._id] = get_usage(node) for contrib in node.contributors: if node.can_edit(user=contrib): users[contrib._id] = tuple(map(sum, zip(users[contrib._id], projects[node._id]))) # Adds tuples together, map(sum, zip((a, b), (c, d))) -> (a+c, b+d) if i % 25 == 0: gc.collect() progress_bar.close() for model, collection, limit in ((OSFUser, users, USER_LIMIT), (AbstractNode, projects, PROJECT_LIMIT)): for item, (used, deleted) in filter(functools.partial(limit_filter, limit), collection.items()): line = '{!r} has exceeded the limit {:.2f}GBs ({}b) with {:.2f}GBs ({}b) used and {:.2f}GBs ({}b) deleted.'.format(model.load(item), limit / GBs, limit, used / GBs, used, deleted / GBs, deleted) logger.info(line) lines.append(line) if lines: if send_email: logger.info('Sending email...') mails.send_mail('*****@*****.**', mails.EMPTY, body='\n'.join(lines), subject='Script: OsfStorage usage audit', can_change_preferences=False,) else: logger.info('send_email is False, not sending email'.format(len(lines))) logger.info('{} offending project(s) and user(s) found'.format(len(lines))) else: logger.info('No offending projects or users found')
def forgot_password_post(): """Attempt to send user password reset or return respective error. """ form = ForgotPasswordForm(request.form, prefix='forgot_password') if form.validate(): email = form.email.data status_message = ( 'If there is an OSF account associated with {0}, an email with instructions on how to reset ' 'the OSF password has been sent to {0}. If you do not receive an email and believe you ' 'should have, please contact OSF Support. ').format(email) user_obj = get_user(email=email) if user_obj: #TODO: Remove this rate limiting and replace it with something that doesn't write to the User model now = datetime.datetime.utcnow() last_attempt = user_obj.forgot_password_last_post or now - datetime.timedelta( seconds=FORGOT_PASSWORD_MINIMUM_TIME) user_obj.forgot_password_last_post = now time_since_last_attempt = now - last_attempt if time_since_last_attempt.seconds >= FORGOT_PASSWORD_MINIMUM_TIME: user_obj.verification_key = security.random_string(20) user_obj.save() reset_link = "http://{0}{1}".format( request.host, web_url_for('reset_password', verification_key=user_obj.verification_key)) mails.send_mail(to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link) status.push_status_message(status_message, 'success') else: user_obj.save() status.push_status_message( 'You have recently requested to change your password. Please wait a little ' 'while before trying again.', 'error') else: status.push_status_message(status_message, 'success') forms.push_errors_to_status(form.errors) return auth_login(forgot_password_form=form)
def set_password(self, raw_password, notify=True): """Set the password for this user to the hash of ``raw_password``. If this is a new user, we're done. If this is a password change, then email the user about the change and clear all the old sessions so that users will have to log in again with the new password. :param raw_password: the plaintext value of the new password :param notify: Only meant for unit tests to keep extra notifications from being sent :rtype: list :returns: Changed fields from the user save """ had_existing_password = self.has_usable_password() if self.username == raw_password: raise ChangePasswordError( ['Password cannot be the same as your email address']) super(OSFUser, self).set_password(raw_password) if had_existing_password and notify: mails.send_mail(to_addr=self.username, mail=mails.PASSWORD_RESET, mimetype='plain', user=self) remove_sessions_for_user(self)
def send_confirm_email(user, email): """Sends a confirmation email to `user` to a given email. :raises: KeyError if user does not have a confirmation token for the given email. """ confirmation_url = user.get_confirmation_url( email, external=True, force=True, ) try: merge_target = User.find_one(Q('emails', 'eq', email)) except NoResultsFound: merge_target = None campaign = campaigns.campaign_for_user(user) # Choose the appropriate email template to use and add existing_user flag if a merge or adding an email. if merge_target: mail_template = mails.CONFIRM_MERGE confirmation_url = '{}?logout=1'.format(confirmation_url) elif campaign: mail_template = campaigns.email_template_for_campaign(campaign) elif user.is_active: mail_template = mails.CONFIRM_EMAIL confirmation_url = '{}?logout=1'.format(confirmation_url) else: mail_template = mails.INITIAL_CONFIRM_EMAIL mails.send_mail( email, mail_template, 'plain', user=user, confirmation_url=confirmation_url, email=email, merge_target=merge_target, )
def main(): prereg_csv = generate_prereg_csv() filename = 'prereg_{}.csv.gz'.format(timezone.now().isoformat()) output = io.BytesIO() with gzip.GzipFile(filename=filename, mode='wb', fileobj=output) as gzip_obj: gzip_obj.write(prereg_csv.getvalue()) mails.send_mail( mail=mails.PREREG_CSV, to_addr=settings.PREREG_EMAIL, attachment_name=filename, attachment_content=output.getvalue(), can_change_preferences=False, logo=settings.OSF_PREREG_LOGO, celery= False # attachment is not JSON-serializable, so don't pass it to celery ) logger.info('Updated prereg CSV email sent.')
def forgot_password(): form = ForgotPasswordForm(request.form, prefix='forgot_password') if form.validate(): email = form.email.data user_obj = get_user(username=email) if user_obj: user_obj.verification_key = security.random_string(20) user_obj.save() reset_link = "http://{0}{1}".format( request.host, web_url_for('reset_password', verification_key=user_obj.verification_key)) mails.send_mail(to_addr=email, mail=mails.FORGOT_PASSWORD, reset_link=reset_link) status.push_status_message('Reset email sent to {0}'.format(email)) else: status.push_status_message( 'Email {email} not found'.format(email=email)) forms.push_errors_to_status(form.errors) return auth_login(forgot_password_form=form)
def deactivate_requested_accounts(dry_run=True): users = OSFUser.objects.filter(requested_deactivation=True, contacted_deactivation=False, date_disabled__isnull=True) for user in users: if user.has_resources: logger.info( 'OSF support is being emailed about deactivating the account of user {}.' .format(user._id)) if not dry_run: mails.send_mail( to_addr=OSF_SUPPORT_EMAIL, mail=mails.REQUEST_DEACTIVATION, user=user, can_change_preferences=False, ) else: logger.info('Disabling user {}.'.format(user._id)) if not dry_run: user.disable_account() user.is_registered = False mails.send_mail( to_addr=user.username, mail=mails.REQUEST_DEACTIVATION_COMPLETE, user=user, contact_email=OSF_CONTACT_EMAIL, can_change_preferences=False, ) user.contacted_deactivation = True user.email_last_sent = timezone.now() if not dry_run: user.save() if dry_run: logger.info('Dry run complete')