Beispiel #1
0
    def test_bounce_custom_message(self):
        import base64
        from repoze.postoffice.message import Message
        class DummySend(object):
            def __init__(self):
                self.sent = []
            def __call__(self, fromaddr, toaddrs, message):
                self.sent.append((fromaddr, toaddrs, message))

        bounced = Message()
        bounced['To'] = 'Submissions <*****@*****.**>'
        bounced['From'] = 'Chris Rossi <*****@*****.**>'
        queue = self._make_one()
        send = DummySend()
        bounce_message = Message()
        bounce_message.set_payload('TEST')
        queue.bounce(bounced, send, 'Bouncer <*****@*****.**>',
                     bounce_message=bounce_message)

        self.assertEqual(len(send.sent), 1)
        fromaddr, toaddrs, message = send.sent[0]
        self.assertEqual(fromaddr, 'Bouncer <*****@*****.**>')
        self.assertEqual(toaddrs, ['Chris Rossi <*****@*****.**>'])
        self.assertEqual(message.get_payload(), 'TEST')
        self.assertEqual(message['X-Postoffice'], 'Bounced')
Beispiel #2
0
    def test_bounce_custom_message(self):
        import base64
        from repoze.postoffice.message import Message

        class DummySend(object):
            def __init__(self):
                self.sent = []

            def __call__(self, fromaddr, toaddrs, message):
                self.sent.append((fromaddr, toaddrs, message))

        bounced = Message()
        bounced['To'] = 'Submissions <*****@*****.**>'
        bounced['From'] = 'Chris Rossi <*****@*****.**>'
        queue = self._make_one()
        send = DummySend()
        bounce_message = Message()
        bounce_message.set_payload('TEST')
        queue.bounce(bounced,
                     send,
                     'Bouncer <*****@*****.**>',
                     bounce_message=bounce_message)

        self.assertEqual(len(send.sent), 1)
        fromaddr, toaddrs, message = send.sent[0]
        self.assertEqual(fromaddr, 'Bouncer <*****@*****.**>')
        self.assertEqual(toaddrs, ['Chris Rossi <*****@*****.**>'])
        self.assertEqual(message.get_payload(), 'TEST')
        self.assertEqual(message['X-Postoffice'], 'Bounced')
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)
Beispiel #4
0
 def __init__(self, context, profile, request):
     from repoze.postoffice.message import Message
     message = Message()
     message["From"] = "Karl <*****@*****.**>"
     message["To"] = "Andy Ex <*****@*****.**>"
     message["Subject"] = "Testing"
     message.set_payload("Test email", "UTF-8")
     self.message = message
Beispiel #5
0
 def __init__(self, context, profile, request):
     from repoze.postoffice.message import Message
     message = Message()
     message["From"] = "Karl <*****@*****.**>"
     message["To"] = "Andy Ex <*****@*****.**>"
     message["Subject"] = "Testing"
     message.set_payload("Test email", "UTF-8")
     self.message = message
Beispiel #6
0
    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)
Beispiel #7
0
    def bounce(self, message, send,
               bounce_from_addr,
               bounce_reason=None,
               bounce_message=None):
        """
        Sends a bounce message to the sender of 'message'. The mechanism for
        mail delivery is passed in via the 'send' argument. 'bounce_from_addr'
        specifices the email address the bounce message should be from. If
        'bounce_reason' is specified, a bounce message will be generated using
        the given bounce reason, a string. If 'bounce_message' is specified,
        the given bounce message is sent, rather than generating a new one.
        'bounce_message' should be an instance of `email.message.Message`.
        If neither 'bounce_reason' or 'bounce_message' is specified, a generic
        bounce message will be sent.  Specifying both is an error.

        The 'send' argument is a callable with the following signature::

            def send(fromaddr, toaddrs, message):
                 "Send an email message."

        Implementations can be found in `repoze.sendmail`.  See
        `repoze.sendmail.delivery.QueuedMailDelivery.send` or
        `repoze.sendmail.delivery.DirectMailDelivery.send`.
        """
        if bounce_reason is not None and bounce_message is not None:
            raise ValueError(
                "May only specify one of either 'bounce_reason' or "
                "'bounce_message'."
            )

        toaddrs = [message['From'],]
        if bounce_message is None:
            if bounce_reason is None:
                bounce_reason = u'Email message is invalid.'
            if 'Date' in message:
                date = message['Date']
            else:
                date = datetime.datetime.now().ctime()
            bounce_to = message['To']
            subject = 'Your message to %s has bounced.' % bounce_to
            bounce_message = Message()
            bounce_message['Subject'] = subject
            bounce_message['From'] = bounce_from_addr
            bounce_message['To'] = message['From']
            body = _default_bounce_body % (date, bounce_to, bounce_reason)
            bounce_message.set_payload(body.encode('UTF-8'), 'UTF-8')

        bounce_message['X-Postoffice'] = 'Bounced'
        send(bounce_from_addr, toaddrs, bounce_message)
Beispiel #8
0
    def quarantine(self, message, error, send=None, notice_from=None):
        """
        Adds a message and corresponding exception info to the 'quarantine'.
        If an application attempts to process a message and encounters an
        exception it may choose to place the message in quarantine such that
        the message and corresponding exception info can be examined by
        developers and/or site administrators.  Once the problem causing the
        exception has been addressed, the messages can be requeued and tried
        again.

        The 'send' argument is optional. If specified it will be used to notify
        the sender of the message that their message has been quarantined. The
        'send' argument is a callable with the following signature::

            def send(fromaddr, toaddrs, message):
                 "Send an email message."

        Implementations can be found in `repoze.sendmail`.  See
        `repoze.sendmail.delivery.QueuedMailDelivery.send` or
        `repoze.sendmail.delivery.DirectMailDelivery.send`.

        If 'send' is specificed, 'notice_from' must also be specified, where
        'notice_from' is the apparent from email address of the notice sent to
        the sender.
        """
        if send is not None and notice_from is None:
            raise ValueError("Must specify 'notice_from' in order to send "
                             "notice.")

        quarantine = self._quarantine
        id = _new_id(quarantine)
        message['X-Postoffice-Id'] = str(id)
        quarantine[id] = (_QueuedMessage(message), error)

        if send is not None:
            notice = Message()
            notice['Subject'] = ('An error has occurred while processing your '
                                 'email to %s' % message['To'])
            notice['From'] = notice_from
            notice['To'] = message['From']
            if 'Date' in message:
                date = message['Date']
            else:
                date = datetime.datetime.now().ctime()
            notice['X-Postoffice'] = 'Bounced'
            body = _quarantine_notice_body % (date, message['To'])
            notice.set_payload(body.encode('UTF-8'), 'UTF-8')
            send(notice_from, [message['From'],], notice)
Beispiel #9
0
    def test_bounce_generic_message(self):
        import base64
        from repoze.postoffice.message import Message

        class DummySend(object):
            def __init__(self):
                self.sent = []

            def __call__(self, fromaddr, toaddrs, message):
                self.sent.append((fromaddr, toaddrs, message))

        bounced = Message()
        bounced['To'] = 'Submissions <*****@*****.**>'
        bounced['From'] = 'Chris Rossi <*****@*****.**>'
        bounced['Date'] = 'Last Tuesday'
        queue = self._make_one()
        send = DummySend()
        queue.bounce(bounced, send, 'Bouncer <*****@*****.**>')

        self.assertEqual(len(send.sent), 1)
        fromaddr, toaddrs, message = send.sent[0]
        self.assertEqual(fromaddr, 'Bouncer <*****@*****.**>')
        self.assertEqual(message['From'], 'Bouncer <*****@*****.**>')
        self.assertEqual(toaddrs, ['Chris Rossi <*****@*****.**>'])
        self.assertEqual(message['To'], 'Chris Rossi <*****@*****.**>')
        self.failUnless(
            message['Subject'].startswith('Your message to Submissions'),
            message['Subject'])
        self.assertEqual(message['X-Postoffice'], 'Bounced')
        body = base64.b64decode(message.get_payload())
        self.failUnless('has bounced' in body, body)
        self.failUnless('Last Tuesday' in body, body)
Beispiel #10
0
    def test_quarantine_notice_w_date(self):
        import base64
        from repoze.postoffice.message import Message

        class DummySend(object):
            def __init__(self):
                self.sent = []

            def __call__(self, fromaddr, toaddrs, message):
                self.sent.append((fromaddr, toaddrs, message))

        message = Message()
        message['To'] = 'Submissions <*****@*****.**>'
        message['From'] = 'Chris Rossi <*****@*****.**>'
        message['Date'] = 'Last Tuesday'
        queue = self._make_one()
        send = DummySend()
        queue.quarantine(message, (None, None, None), send,
                         'Oopsy Daisy <*****@*****.**>')
        self.assertEqual(len(send.sent), 1)
        fromaddr, toaddrs, notice = send.sent[0]
        self.assertEqual(fromaddr, 'Oopsy Daisy <*****@*****.**>')
        self.assertEqual(notice['From'], 'Oopsy Daisy <*****@*****.**>')
        self.assertEqual(toaddrs, ['Chris Rossi <*****@*****.**>'])
        self.assertEqual(notice['To'], 'Chris Rossi <*****@*****.**>')
        self.failUnless(notice['Subject'].startswith('An error has occurred'))
        self.assertEqual(notice['X-Postoffice'], 'Bounced')
        body = base64.b64decode(notice.get_payload())
        self.failUnless('System administrators have been informed' in body)
        self.failUnless('Last Tuesday' in body)
Beispiel #11
0
    def send_digests(self, context):
        mailer = getUtility(IMailDelivery)

        system_name = get_setting(context, "system_name", "KARL")
        system_email_domain = get_setting(context, "system_email_domain")
        sent_from = "%s@%s" % (system_name, system_email_domain)
        from_addr = "%s <%s>" % (system_name, sent_from)
        subject = "[%s] Your alerts digest" % system_name

        template = get_renderer("email_digest.pt").implementation()
        for profile in find_profiles(context).values():
            if not profile._pending_alerts:
                continue

            # Perform each in its own transaction, so a problem with one
            # user's email doesn't block all others
            transaction.manager.begin()
            try:
                attachments = []
                for alert in profile._pending_alerts:
                    attachments += alert['attachments']

                msg = MIMEMultipart() if attachments else Message()
                msg["From"] = from_addr
                msg["To"] = "%s <%s>" % (profile.title, profile.email)
                msg["Subject"] = subject

                body_text = template.render(
                    system_name=system_name,
                    alerts=profile._pending_alerts
                )

                if isinstance(body_text, unicode):
                    body_text = body_text.encode("UTF-8")

                if attachments:
                    body = MIMEText(body_text, 'html', 'utf-8')
                    msg.attach(body)
                else:
                    msg.set_payload(body_text, "UTF-8")
                    msg.set_type("text/html")

                for attachment in attachments:
                    msg.attach(attachment)

                mailer.send([profile.email,], msg)
                del profile._pending_alerts[:]
                transaction.manager.commit()

            except Exception, e:
                # Log error and continue
                log.error("Error sending digest to %s <%s>" %
                          (profile.title, profile.email))

                b = StringIO()
                traceback.print_exc(file=b)
                log.error(b.getvalue())
                b.close()

                transaction.manager.abort()
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
Beispiel #13
0
    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)
Beispiel #14
0
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)
Beispiel #15
0
    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
        )
Beispiel #16
0
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
Beispiel #17
0
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
Beispiel #18
0
 def __init__(self, body=None):
     Message.__init__(self)
     self['From'] = 'Harry'
     self['Message-Id'] = '12345'
     self.set_payload(body)
Beispiel #19
0
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)
Beispiel #20
0
 def __init__(self, body=None):
     Message.__init__(self)
     self['From'] = 'Harry'
     self['Message-Id'] = '12345'
     self['X-Original-To'] = '*****@*****.**'
     self.set_payload(body)
Beispiel #21
0
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)
Beispiel #22
0
    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,
        )
Beispiel #23
0
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)
Beispiel #24
0
    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,
        )
Beispiel #25
0
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
Beispiel #26
0
    def send_digests(self, context, period='daily'):
        PERIODS = {
            'daily': [IProfile.ALERT_DAILY_DIGEST],
            'weekly':
            [IProfile.ALERT_DAILY_DIGEST, IProfile.ALERT_WEEKLY_DIGEST],
            'biweekly': [
                IProfile.ALERT_DAILY_DIGEST, IProfile.ALERT_WEEKLY_DIGEST,
                IProfile.ALERT_BIWEEKLY_DIGEST
            ],
        }
        periods = PERIODS[period]
        mailer = getUtility(IMailDelivery)

        system_name = get_setting(context, "title", "KARL")
        sent_from = get_setting(context, "admin_email")
        from_addr = "%s <%s>" % (system_name, sent_from)
        subject = "[%s] Your alerts digest" % system_name

        template = get_renderer("email_digest.pt").implementation()
        for profile in find_profiles(context).values():
            if not list(profile._pending_alerts):
                continue

            # Perform each in its own transaction, so a problem with one
            # user's email doesn't block all others
            transaction.manager.begin()
            alerts = profile._pending_alerts.consume()
            try:
                pending = []
                skipped = []
                for alert in alerts:
                    community = alert.get('community')
                    if community is not None:
                        pref = profile.get_alerts_preference(community)
                        if pref in periods:
                            pending.append(alert)
                        else:
                            skipped.append(alert)
                    else:  # XXX belt-and-suspenders:  send it now
                        pending.append(alert)

                if len(pending) > 0:

                    attachments = []
                    for alert in pending:
                        attachments += alert['attachments']

                    msg = MIMEMultipart() if attachments else Message()
                    msg["From"] = from_addr
                    msg["To"] = "%s <%s>" % (profile.title, profile.email)
                    msg["Subject"] = subject

                    body_text = template.render(
                        system_name=system_name,
                        alerts=pending,
                    )

                    if isinstance(body_text, unicode):
                        body_text = body_text.encode("UTF-8")

                    if attachments:
                        body = MIMEText(body_text, 'html', 'utf-8')
                        msg.attach(body)
                    else:
                        msg.set_payload(body_text, "UTF-8")
                        msg.set_type("text/html")

                    for attachment in attachments:
                        msg.attach(attachment)

                    mailer.send([profile.email], msg)

                for alert in skipped:
                    profile._pending_alerts.append(alert)

                transaction.manager.commit()

            except Exception:
                # Log error and continue
                log.error("Error sending digest to %s <%s>" %
                          (profile.title, profile.email))

                b = StringIO()
                traceback.print_exc(file=b)
                log.error(b.getvalue())
                b.close()

                transaction.manager.abort()
Beispiel #27
0
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
Beispiel #28
0
    def message(self):
        if self._message is not None:
            return self._message

        community = self._community
        request = self.request
        profile = self.profile
        blogentry = self._blogentry

        community_href = resource_url(community, request)
        blogentry_href = resource_url(blogentry, request)
        manage_preferences_href = resource_url(profile, request)
        system_name = get_setting(self.context, "system_name", "KARL")
        system_email_domain = get_setting(self.context, "system_email_domain")

        reply_to = '"%s" <%s+blog-%s@%s>' % (
            community.title, community.__name__, docid_to_hex(
                blogentry.docid), system_email_domain)

        attachments, attachment_links, attachment_hrefs = self.attachments

        body_template = get_renderer(self._template).implementation()
        from_name = "%s | %s" % (self.creator.title, system_name)
        msg = MIMEMultipart() if attachments else Message()
        msg["From"] = '"%s" <%s>' % (from_name, self.mfrom)
        msg["To"] = '"%s" <%s>' % (profile.title, profile.email)
        msg["Reply-to"] = reply_to
        msg["Subject"] = self._subject
        msg["Precedence"] = 'bulk'
        body_text = body_template(
            context=self.context,
            community=community,
            community_href=community_href,
            blogentry=blogentry,
            blogentry_href=blogentry_href,
            attachments=attachment_links,
            attachment_hrefs=attachment_hrefs,
            manage_preferences_href=manage_preferences_href,
            profile=profile,
            profiles=self.profiles,
            creator=self.creator,
            digest=self.digest,
            alert=self,
            history=self._history,
        )

        if self.digest:
            # Only interested in body for digest
            html = document_fromstring(body_text)
            body_element = html.cssselect('body')[0]
            span = etree.Element("span", nsmap=body_element.nsmap)
            span[:] = body_element[:]  # Copy all body elements to an empty span
            body_text = etree.tostring(span, pretty_print=True)

        if isinstance(body_text, unicode):
            body_text = body_text.encode('utf-8')

        if attachments:
            body = MIMEText(body_text, 'html', 'utf-8')
            msg.attach(body)
            for attachment in attachments:
                msg.attach(attachment)
        else:
            msg.set_payload(body_text, 'utf-8')
            msg.set_type("text/html")

        self._message = msg

        return self._message
Beispiel #29
0
 def __init__(self, body=None):
     Message.__init__(self)
     self['From'] = 'Harry'
     self['Message-Id'] = '12345'
     self['X-Original-To'] = '*****@*****.**'
     self.set_payload(body)
Beispiel #30
0
    def message(self):
        if self._message is not None:
            return self._message

        community = self._community
        request = self.request
        profile = self.profile
        model = self._model

        community_href = resource_url(community, request)
        model_href = resource_url(model, request)
        manage_preferences_href = resource_url(
            profile, request) + '/manage_communities.html'  # noqa
        system_name = get_setting(self.context, "title", "KARL")

        attachments, attachment_links, attachment_hrefs = self.attachments

        from_name = "%s | %s" % (self.creator.title, system_name)
        msg = MIMEMultipart() if attachments else Message()
        msg["From"] = '"%s" <%s>' % (from_name, self.mfrom)
        msg["To"] = '"%s" <%s>' % (community.title, profile.email)
        msg["Subject"] = self._subject
        msg["Precedence"] = 'bulk'
        body_text = self.template(
            context=self.context,
            community=community,
            community_href=community_href,
            model=model,
            model_href=model_href,
            manage_preferences_href=manage_preferences_href,
            attachments=attachment_links,
            attachment_hrefs=attachment_hrefs,
            profile=profile,
            profiles=self.profiles,
            creator=self.creator,
            content_type=self._content_type_name,
            digest=self.digest,
            alert=self,
            resource_url=resource_url,
            request=request,
            reply_enabled=self.reply_enabled)

        if self.digest:
            # Only interested in body for digest
            html = document_fromstring(body_text)
            body_element = html.cssselect('body')[0]
            span = etree.Element("span", nsmap=body_element.nsmap)
            span[:] = body_element[:]  # Copy all body elements to an empty span
            body_text = etree.tostring(span, pretty_print=True)

        if isinstance(body_text, unicode):
            body_text = body_text.encode('utf-8')

        if attachments:
            body = MIMEText(body_text, 'html', 'utf-8')
            msg.attach(body)
            for attachment in attachments:
                msg.attach(attachment)
        else:
            msg.set_payload(body_text, 'utf-8')
            msg.set_type("text/html")

        self._message = msg
        return msg