def request_password_reset(user, profile, request): profile.password_reset_key = sha1( str(random.random())).hexdigest() profile.password_reset_time = datetime.datetime.now() context = find_site(profile) reset_url = resource_url( context, request, "reset_confirm.html", query=dict(key=profile.password_reset_key)) # send email mail = Message() system_name = get_setting(context, 'system_name', 'KARL') admin_email = get_setting(context, 'admin_email') mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "%s Password Reset Request" % system_name body = render( "templates/email_reset_password.pt", dict(login=user['login'], reset_url=reset_url, system_name=system_name), request=request, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail)
def bounce_message_throttled(self, message): mailer = getUtility(IMailDelivery) from_email = get_setting(self.root, 'postoffice.bounce_from_email') if from_email is None: from_email = get_setting(self.root, 'admin_email') bounce_message = Message() bounce_message['From'] = from_email bounce_message['To'] = message['From'] bounce_message['Subject'] = 'Your submission to Karl has bounced.' bounce_message.set_type('text/html') bounce_message.set_payload( render( 'templates/bounce_email_throttled.pt', dict( subject=message.get('Subject'), system_name=get_setting(self.root, 'system_name', 'KARL'), admin_email=get_setting(self.root, 'admin_email'), ), ).encode('UTF-8'), 'UTF-8') self.queue.bounce(message, wrap_send(mailer.bounce), from_email, bounce_message=bounce_message)
def request_password_reset(user, profile, request): profile.password_reset_key = sha1(str(random.random())).hexdigest() profile.password_reset_time = datetime.datetime.now() context = find_site(profile) reset_url = resource_url(context, request, "reset_confirm.html", query=dict(key=profile.password_reset_key)) # send email mail = Message() system_name = get_setting(context, 'system_name', 'KARL') admin_email = get_setting(context, 'admin_email') mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "%s Password Reset Request" % system_name body = render( "templates/email_reset_password.pt", dict(login=user['login'], reset_url=reset_url, system_name=system_name), request=request, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail)
def __call__(self): context, request = self.context, self.request api = AdminTemplateAPI(context, request, "Admin UI: Send Email") admin_email = get_setting(context, "admin_email") system_name = get_setting(context, "system_name") profiles = find_profiles(context) admin = profiles[authenticated_userid(request)] from_emails = [ ("self", "%s <%s>" % (admin.title, admin.email)), ("admin", "%s Administrator <%s>" % (system_name, admin_email)), ] if "send_email" in request.params: mailer = getUtility(IMailDelivery) group = request.params["to_group"] users = find_users(context) search = ICatalogSearch(context) count, docids, resolver = search(interfaces=[IProfile]) n = 0 for docid in docids: profile = resolver(docid) if getattr(profile, "security_state", None) == "inactive": continue userid = profile.__name__ if group and not users.member_of_group(userid, group): continue message = Message() if request.params["from_email"] == "self": message["From"] = from_emails[0][1] message_from = admin.email else: message["From"] = from_emails[1][1] message_from = admin_email message["To"] = "%s <%s>" % (profile.title, profile.email) message["Subject"] = request.params["subject"] body = u"<html><body>%s</body></html>" % (request.params["text"]) message.set_payload(body.encode("UTF-8"), "UTF-8") message.set_type("text/html") mailer.send([profile.email], message) n += 1 status_message = "Sent message to %d users." % n if has_permission(ADMINISTER, context, request): redirect_to = model_url(context, request, "admin.html", query=dict(status_message=status_message)) else: redirect_to = model_url( find_communities(context), request, "all_communities.html", query=dict(status_message=status_message), ) return HTTPFound(location=redirect_to) return dict(api=api, menu=_menu_macro(), to_groups=self.to_groups, from_emails=from_emails)
def login_user(request, profile, login, userid): site = request.root admin_only = asbool(request.registry.settings.get('admin_only', '')) admins = aslist(request.registry.settings.get('admin_userids', '')) if admin_only and userid not in admins: return site_down_view(site, request) settings = request.registry.settings device_cookie_name = settings.get('device_cookie', 'CxR61DzG3P0Ae1') max_age = settings.get('login_cookie_max_age', '36000') max_age = int(max_age) response = remember_login(site, request, userid, max_age) # have we logged in from this computer & browser before? active_device = request.cookies.get(device_cookie_name, None) if active_device is None and profile is not None: # if not, send email reset_url = request.resource_url(profile, 'change_password.html') mail = Message() system_name = settings.get('system_name', 'KARL') admin_email = settings.get('admin_email') mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "New %s Login Notification" % system_name user_agent = user_agents.parse(request.user_agent) body = render( "templates/email_suspicious_login.pt", dict(login=login, reset_url=reset_url, device_info=user_agent), request=request, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail) # set cookie to avoid further notifications for this device active_device = ''.join( random.choice(string.ascii_uppercase + string.digits) for _ in range(16)) response.set_cookie(device_cookie_name, active_device, max_age=315360000) if profile is not None: profile.active_device = active_device request.session['logout_reason'] = None return response
def send_reminders(env, start, end): print print "Sending reminders for passwords expiring between %s and %s" % ( start.strftime('%Y-%m-%d %H:%M'), end.strftime('%Y-%m-%d %H:%M')) print root = env['root'] registry = env['registry'] request = env['request'] system_name = get_setting(root, 'system_name', 'KARL') admin_email = get_setting(root, 'admin_email') site_url = get_setting(root, 'script_app_url') reset_url = "%s/reset_request.html" % site_url profiles = find_profiles(root) for profile in profiles.values(): auth_method = profile.auth_method.lower() if auth_method != 'password': continue expiration = profile.password_expiration_date if expiration > start and expiration < end: mail = Message() mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "%s Password Expiration Reminder" % system_name body = render( "templates/password_expiration_reminder.pt", dict(login=profile.__name__, reset_url=reset_url, expiration_date=start.strftime('%Y-%m-%d'), system_name=system_name), request=request, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail) print "Sent reminder to user '%s'" % profile.title.encode("UTF-8") print
def send_reminders(env, start, end): print print "Sending reminders for passwords expiring between %s and %s" % ( start.strftime('%Y-%m-%d %H:%M'), end.strftime('%Y-%m-%d %H:%M')) print root = env['root'] registry = env['registry'] request = env['request'] system_name = get_setting(root, 'system_name', 'KARL') admin_email = get_setting(root, 'admin_email') site_url = get_setting(root, 'script_app_url') reset_url = "%s/reset_request.html" % site_url profiles = find_profiles(root) for profile in profiles.values(): expiration = profile.password_expiration_date if expiration > start and expiration < end: mail = Message() mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "%s Password Expiration Reminder" % system_name body = render( "templates/password_expiration_reminder.pt", dict(login=profile.__name__, reset_url=reset_url, expiration_date=start.strftime('%Y-%m-%d'), system_name=system_name), request=request, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail) print "Sent reminder to user '%s'" % profile.title print
def bounce_message_throttled(self, message): mailer = getUtility(IMailDelivery) from_email = get_setting(self.root, 'postoffice.bounce_from_email') if from_email is None: from_email = get_setting(self.root, 'admin_email') bounce_message = Message() bounce_message['From'] = from_email bounce_message['To'] = message['From'] bounce_message['Subject'] = 'Your submission to Karl has bounced.' bounce_message.set_type('text/html') bounce_message.set_payload(render_template( 'templates/bounce_email_throttled.pt', subject=message.get('Subject'), system_name=get_setting(self.root, 'system_name', 'KARL'), admin_email=get_setting(self.root, 'admin_email'), ).encode('UTF-8'), 'UTF-8') self.queue.bounce( message, wrap_send(mailer.send), from_email, bounce_message=bounce_message )
def join_community_view(context, request): """ User sends an email to community moderator(s) asking to join the community. Email contains a link to "add_existing" view, in members, that a moderator can use to add member to the community. """ assert ICommunity.providedBy(context) # Get logged in user profiles = find_profiles(context) user = authenticated_userid(request) profile = profiles[user] # Handle form submission if "form.submitted" in request.POST: message = request.POST.get("message", "") moderators = [profiles[id] for id in context.moderator_names] mail = Message() mail["From"] = "%s <%s>" % (profile.title, profile.email) mail["To"] = ",".join( ["%s <%s>" % (p.title, p.email) for p in moderators]) mail["Subject"] = "Request to join %s community" % context.title body_template = get_renderer( "templates/email_join_community.pt").implementation() profile_url = resource_url(profile, request) accept_url = resource_url(context, request, "members", "add_existing.html", query={"user_id": user}) body = body_template(message=message, community_title=context.title, person_name=profile.title, profile_url=profile_url, accept_url=accept_url) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [p.email for p in moderators] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail) status_message = "Your request has been sent to the moderators." location = resource_url(context, request, query={"status_message": status_message}) return HTTPFound(location=location) # Show form page_title = "Join " + context.title api = TemplateAPI(context, request, page_title) return dict(api=api, profile=profile, community=context, post_url=resource_url(context, request, "join.html"), formfields=api.formfields)
def join_community_view(context, request): """ User sends an email to community moderator(s) asking to join the community. Email contains a link to "add_existing" view, in members, that a moderator can use to add member to the community. """ assert ICommunity.providedBy(context) # Get logged in user profiles = find_profiles(context) user = authenticated_userid(request) profile = profiles[user] # Handle form submission if "form.submitted" in request.POST: message = request.POST.get("message", "") moderators = [profiles[id] for id in context.moderator_names] mail = Message() mail["From"] = "%s <%s>" % (profile.title, profile.email) mail["To"] = ",".join( ["%s <%s>" % (p.title, p.email) for p in moderators] ) mail["Subject"] = "Request to join %s community" % context.title body_template = get_renderer( "templates/email_join_community.pt").implementation() profile_url = resource_url(profile, request) accept_url=resource_url(context, request, "members", "add_existing.html", query={"user_id": user}) body = body_template( message=message, community_title=context.title, person_name=profile.title, profile_url=profile_url, accept_url=accept_url ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [p.email for p in moderators] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail) status_message = "Your request has been sent to the moderators." location = resource_url(context, request, query={"status_message": status_message}) return HTTPFound(location=location) # Show form page_title = "Join " + context.title api = TemplateAPI(context, request, page_title) return dict(api=api, profile=profile, community=context, post_url=resource_url(context, request, "join.html"), formfields=api.formfields)
def __call__(self): context, request = self.context, self.request request.layout_manager.use_layout('admin') api = AdminTemplateAPI(context, request, 'Admin UI: Send Email') admin_email = get_setting(context, 'admin_email') system_name = get_setting(context, 'system_name') profiles = find_profiles(context) admin = profiles[authenticated_userid(request)] from_emails = [ ('self', '%s <%s>' % (admin.title, admin.email)), ('admin', '%s Administrator <%s>' % (system_name, admin_email)), ] if 'send_email' in request.params or 'submit' in request.params: mailer = getUtility(IMailDelivery) group = request.params['to_group'] users = find_users(context) search = ICatalogSearch(context) count, docids, resolver = search(interfaces=[IProfile]) n = 0 for docid in docids: profile = resolver(docid) if getattr(profile, 'security_state', None) == 'inactive': continue userid = profile.__name__ if group and not users.member_of_group(userid, group): continue message = Message() if request.params['from_email'] == 'self': message['From'] = from_emails[0][1] message_from = admin.email else: message['From'] = from_emails[1][1] message_from = admin_email message['To'] = '%s <%s>' % (profile.title, profile.email) message['Subject'] = request.params['subject'] body = u'<html><body>%s</body></html>' % ( request.params['text'] ) message.set_payload(body.encode('UTF-8'), 'UTF-8') message.set_type('text/html') mailer.send([profile.email], message) n += 1 status_message = "Sent message to %d users." % n if has_permission(ADMINISTER, context, request): redirect_to = resource_url( context, request, 'admin.html', query=dict(status_message=status_message)) else: redirect_to = resource_url( find_communities(context), request, 'all_communities.html', query=dict(status_message=status_message)) return HTTPFound(location=redirect_to) return dict( api=api, menu=_menu_macro(), to_groups = self.to_groups, from_emails=from_emails, )
def login_view(context, request): settings = request.registry.settings came_from = request.session.get('came_from', request.url) if 'login.html' in came_from or 'logout.html' in came_from: came_from = request.application_url request.session['came_from'] = came_from submitted = request.params.get('form.submitted', None) if submitted: # identify login = request.POST.get('login') password = request.POST.get('password') if login is None or password is None: return HTTPFound(location='%s/login.html' % request.application_url) max_retries = request.registry.settings.get('max_login_retries', 8) left = context.login_tries.get(login, max_retries) left = left - 1 users = find_users(context) user = users.get_by_login(login) profiles = find_profiles(context) profile = profiles.get(user['id']) if user else None # max tries almost reached, send email warning if left == 2 and profile is not None: reset_url = request.resource_url(profile, 'change_password.html') mail = Message() system_name = settings.get('system_name', 'KARL') admin_email = settings.get('admin_email') mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "Too many failed logins to %s" % system_name body = render( "templates/email_locked_warning.pt", dict(login=login, reset_url=reset_url, system_name=system_name), request=request, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail) # if max tries reached, send password reset and lock if left < 1: log_failed_login(request, login) # only send email the first time if profile is not None and left == 0: context.login_tries[login] = -1 request_password_reset(user, profile, request) page_title = 'Access to %s is locked' % settings.get( 'system_name', 'KARL') api = TemplateAPI(context, request, page_title) response = render_to_response('templates/locked.pt', dict( api=api, app_url=request.application_url), request=request) return response # authenticate reason = 'Bad username or password.' try: userid = _authenticate(context, login, password) except TypeError: userid = None # if not successful, try again if not userid: log_failed_login(request, login) reason = "%s You have %d attempts left." % (reason, left) context.login_tries[login] = left redirect = request.resource_url(request.root, 'login.html', query={'reason': reason}) return HTTPFound(location=redirect) # all ok, remember context.login_tries[login] = max_retries return login_user(request, profile, login, userid) page_title = 'Login to %s' % settings.get( 'system_name', 'KARL') # Per #366377, don't say what screen api = TemplateAPI(context, request, page_title) status_message = request.params.get('reason', None) if status_message != '@@@one-session-only@@@': api.status_message = status_message status_message = None response = render_to_response('templates/login.pt', dict(api=api, status_message=status_message, app_url=request.application_url), request=request) forget_headers = forget(request) response.headers.extend(forget_headers) return response
def login_view(context, request): settings = request.registry.settings came_from = request.session.get('came_from', request.url) came_from = _fixup_came_from(request, came_from) request.session['came_from'] = came_from submitted = request.params.get('form.submitted', None) if submitted: # identify login = request.POST.get('login') password = request.POST.get('password') if login is None or password is None: return HTTPFound(location='%s/login.html' % request.application_url) max_age = request.registry.settings.get('login_cookie_max_age', '36000') max_age = int(max_age) max_retries = request.registry.settings.get('max_login_retries', 8) left = context.login_tries.get(login, max_retries) left = left - 1 profiles = find_profiles(context) profile = profiles.get(login) # max tries almost reached, send email warning if left == 2 and profile is not None: reset_url = request.resource_url(profile, 'change_password.html') mail = Message() system_name = settings.get('system_name', 'KARL') admin_email = settings.get('admin_email') mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "Too many failed logins to %s" % system_name body = render( "templates/email_locked_warning.pt", dict(login=login, reset_url=reset_url, system_name=system_name), request=request, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail) # if max tries reached, send password reset and lock if left < 1: # only send email the first time if profile is not None and left == 0: context.login_tries[login] = -1 users = find_users(context) user = users.get_by_id(login) request_password_reset(user, profile, request) page_title = 'Access to %s is locked' % settings.get('system_name', 'KARL') api = TemplateAPI(context, request, page_title) response = render_to_response( 'templates/locked.pt', dict( api=api, app_url=request.application_url), request=request) return response # authenticate reason = 'Bad username or password.' userid = _authenticate(context, login, password) # if not successful, try again if not userid: reason = "%s You have %d attempts left." % (reason, left) context.login_tries[login] = left redirect = request.resource_url( request.root, 'login.html', query={'reason': reason}) return HTTPFound(location=redirect) device_cookie_name = request.registry.settings.get('device_cookie', 'CxR61DzG3P0Ae1') # all ok, remember admin_only = asbool(request.registry.settings.get('admin_only', '')) admins = aslist(request.registry.settings.get('admin_userids', '')) if not admin_only or userid in admins: context.login_tries[login] = max_retries response = remember_login(context, request, userid, max_age) # have we logged in from this computer & browser before? active_device = request.cookies.get(device_cookie_name, None) if active_device is None: # if not, send email reset_url = request.resource_url(profile, 'change_password.html') mail = Message() system_name = settings.get('system_name', 'KARL') admin_email = settings.get('admin_email') mail["From"] = "%s Administrator <%s>" % (system_name, admin_email) mail["To"] = "%s <%s>" % (profile.title, profile.email) mail["Subject"] = "New %s Login Notification" % system_name # TODO Carlos needs to come back and get this working # https://bugs.launchpad.net/karl4/+bug/1648569/comments/10 # user_agent = user_agents.parse(request.user_agent) body = render( "templates/email_suspicious_login.pt", dict(login=login, reset_url=reset_url, device_info=request.user_agent), request=request, ) if isinstance(body, unicode): body = body.encode("UTF-8") mail.set_payload(body, "UTF-8") mail.set_type("text/html") recipients = [profile.email] mailer = getUtility(IMailDelivery) mailer.send(recipients, mail) # set cookie to avoid further notifications for this device active_device = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(16)) response.set_cookie(device_cookie_name, active_device, max_age=315360000) profile.active_device = active_device request.session['logout_reason'] = None return response else: return site_down_view(context, request) page_title = 'Login to %s' % settings.get('system_name', 'KARL') # Per #366377, don't say what screen api = TemplateAPI(context, request, page_title) status_message = request.params.get('reason', None) if status_message != '@@@one-session-only@@@': api.status_message = status_message status_message = None response = render_to_response( 'templates/login.pt', dict( api=api, status_message = status_message, app_url=request.application_url), request=request) forget_headers = forget(request) response.headers.extend(forget_headers) return response
def __call__(self): context, request = self.context, self.request api = AdminTemplateAPI(context, request, 'Admin UI: Send Email') admin_email = get_setting(context, 'admin_email') system_name = get_setting(context, 'system_name') profiles = find_profiles(context) admin = profiles[authenticated_userid(request)] from_emails = [ ('self', '%s <%s>' % (admin.title, admin.email)), ('admin', '%s Administrator <%s>' % (system_name, admin_email)), ] if 'send_email' in request.params: mailer = getUtility(IMailDelivery) group = request.params['to_group'] users = find_users(context) search = ICatalogSearch(context) count, docids, resolver = search(interfaces=[IProfile]) n = 0 for docid in docids: profile = resolver(docid) if getattr(profile, 'security_state', None) == 'inactive': continue userid = profile.__name__ if group and not users.member_of_group(userid, group): continue message = Message() if request.params['from_email'] == 'self': message['From'] = from_emails[0][1] message_from = admin.email else: message['From'] = from_emails[1][1] message_from = admin_email message['To'] = '%s <%s>' % (profile.title, profile.email) message['Subject'] = request.params['subject'] body = u'<html><body>%s</body></html>' % ( request.params['text']) message.set_payload(body.encode('UTF-8'), 'UTF-8') message.set_type('text/html') mailer.send([profile.email], message) n += 1 status_message = "Sent message to %d users." % n if has_permission(ADMINISTER, context, request): redirect_to = resource_url( context, request, 'admin.html', query=dict(status_message=status_message)) else: redirect_to = resource_url( find_communities(context), request, 'all_communities.html', query=dict(status_message=status_message)) return HTTPFound(location=redirect_to) return dict( api=api, menu=_menu_macro(), to_groups=self.to_groups, from_emails=from_emails, )
def make_non_staff(profile, inform_moderators=True): """ When a user is removed from the KarlStaff role, their community memberships are removed. Moderators of their communities are optionally informed via email. """ id = profile.__name__ moderators = {} users = find_users(profile) profile.categories = {} for group in list(users.get_by_id(id)['groups']): if group.startswith('group.community'): # Remove user from group users.remove_user_from_group(id, group) if not inform_moderators: continue # Keep track of moderators we need to email making sure # each moderator is emailed only once and each community is # only mentioned once in any given email. community_name = group.split(':')[1] moderators_group = ('group.community:%s:moderators' % community_name) for moderator in users.users_in_group(moderators_group): if moderator == id: continue # Really should only come up in unittests if moderator not in moderators: moderators[moderator] = set() moderators[moderator].add(community_name) if not inform_moderators: return communities = find_communities(profile) profiles = profile.__parent__ mailer = getUtility(IMailDelivery) for moderator_id in moderators: moderator = profiles[moderator_id] msg = Message() msg['From'] = get_setting(profile, 'admin_email') msg['To'] = '%s <%s>' % (moderator.title, moderator.email) msg['Subject'] = 'Notice that %s is now former staff' % profile.title former_communities = sorted( [communities[c] for c in moderators[moderator_id]], key=lambda x: x.title) app_url = get_setting(profile, 'offline_app_url') communities_info = [ dict(title=c.title, unremove_url='%s%s?user_id=%s' % (app_url, model_path(c, 'members', 'add_existing.html'), id)) for c in former_communities ] body = render( 'templates/email_notify_former_staff.pt', dict(name=profile.title, communities=communities_info), ) if isinstance(body, unicode): body = body.encode('UTF-8') msg.set_payload(body, 'UTF-8') msg.set_type('text/html') mailer.send([msg['To']], msg)