Exemplo n.º 1
0
    def POST_message(self, level, logs):
        # Whitelist tags to keep the frontend from creating too many keys in statsd
        valid_frontend_log_tags = {
            'unknown',
            'jquery-migrate-bad-html',
        }

        # prevent simple CSRF by requiring a custom header
        if not request.headers.get('X-Loggit'):
            abort(403)

        uid = c.user._id if c.user_is_loggedin else '-'

        # only accept a maximum of 3 entries per request
        for log in logs[:3]:
            if 'msg' not in log or 'url' not in log:
                continue

            tag = 'unknown'

            if log.get('tag') in valid_frontend_log_tags:
                tag = log['tag']

            g.stats.simple_event('frontend.error.' + tag)

            g.log.warning('[web frontend] %s: %s | U: %s FP: %s UA: %s', level,
                          log['msg'], uid, log['url'], request.user_agent)

        VRatelimit.ratelimit(rate_user=False,
                             rate_ip=True,
                             prefix="rate_weblog_",
                             seconds=10)
    def POST_create(self, form, jquery, title, description, resources, nsfw):
        """Create a new live thread.

        Once created, the initial settings can be modified with
        [/api/live/*thread*/edit](#POST_api_live_{thread}_edit) and new updates
        can be posted with
        [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """
        if not is_event_configuration_valid(form):
            return

        if form.has_errors("ratelimit", errors.RATELIMIT):
            return
        VRatelimit.ratelimit(
            rate_user=True, prefix="liveupdate_create_", seconds=60)

        event = LiveUpdateEvent.new(
            id=None,
            title=title,
            description=description,
            resources=resources,
            banned=c.user._spam,
            nsfw=nsfw,
        )
        event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER)
        queries.create_event(event)

        form.redirect("/live/" + event._id)
        form._send_data(id=event._id)
    def POST_create(self, form, jquery, title, description, resources, nsfw):
        """Create a new live thread.

        Once created, the initial settings can be modified with
        [/api/live/*thread*/edit](#POST_api_live_{thread}_edit) and new updates
        can be posted with
        [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """
        if not is_event_configuration_valid(form):
            return

        if form.has_errors("ratelimit", errors.RATELIMIT):
            return
        VRatelimit.ratelimit(
            rate_user=True, prefix="liveupdate_create_", seconds=60)

        event = LiveUpdateEvent.new(
            id=None,
            title=title,
            description=description,
            resources=resources,
            banned=c.user._spam,
            nsfw=nsfw,
        )
        event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER)
        queries.create_event(event)

        form.redirect("/live/" + event._id)
        form._send_data(id=event._id)
Exemplo n.º 4
0
    def POST_message(self, level, logs):
        # Whitelist tags to keep the frontend from creating too many keys in statsd
        valid_frontend_log_tags = {
            'unknown',
            'jquery-migrate-bad-html',
        }

        # prevent simple CSRF by requiring a custom header
        if not request.headers.get('X-Loggit'):
            abort(403)

        uid = c.user._id if c.user_is_loggedin else '-'

        # only accept a maximum of 3 entries per request
        for log in logs[:3]:
            if 'msg' not in log or 'url' not in log:
                continue

            tag = 'unknown'

            if log.get('tag') in valid_frontend_log_tags:
                tag = log['tag']

            g.stats.simple_event('frontend.error.' + tag)

            g.log.warning('[web frontend] %s: %s | U: %s FP: %s UA: %s',
                          level, log['msg'], uid, log['url'],
                          request.user_agent)

        VRatelimit.ratelimit(rate_user=False, rate_ip=True,
                             prefix="rate_weblog_", seconds=10)
Exemplo n.º 5
0
    def POST_message(self, level, logs):
        # Whitelist tags to keep the frontend from creating too many keys in statsd
        valid_frontend_log_tags = {"unknown", "jquery-migrate-bad-html"}

        # prevent simple CSRF by requiring a custom header
        if not request.headers.get("X-Loggit"):
            abort(403)

        uid = c.user._id if c.user_is_loggedin else "-"

        # only accept a maximum of 3 entries per request
        for log in logs[:3]:
            if "msg" not in log or "url" not in log:
                continue

            tag = "unknown"

            if log.get("tag") in valid_frontend_log_tags:
                tag = log["tag"]

            g.stats.simple_event("frontend.error." + tag)

            g.log.warning(
                "[web frontend] %s: %s | U: %s FP: %s UA: %s", level, log["msg"], uid, log["url"], request.user_agent
            )

        VRatelimit.ratelimit(rate_user=False, rate_ip=True, prefix="rate_weblog_", seconds=10)
Exemplo n.º 6
0
    def POST_message(self, level, logs):
        # prevent simple CSRF by requiring a custom header
        if not request.headers.get('X-Loggit'):
            abort(403)

        uid = c.user._id if c.user_is_loggedin else '-'

        # only accept a maximum of 3 entries per request
        for log in logs[:3]:
            g.log.warning('[web frontend] %s: %s | U: %s FP: %s UA: %s',
                          level, log['msg'], uid, log['url'],
                          request.user_agent)

        VRatelimit.ratelimit(rate_user=False, rate_ip=True,
                             prefix="rate_weblog_", seconds=10)
Exemplo n.º 7
0
    def POST_create(self, form, jquery, title, description, resources, nsfw):
        """Create a new live thread.

        Once created, the initial settings can be modified with
        [/api/live/*thread*/edit](#POST_api_live_{thread}_edit) and new updates
        can be posted with
        [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """
        if not is_event_configuration_valid(form):
            return

        # for simplicity, set the live-thread creation threshold at the
        # subreddit creation threshold
        if not c.user_is_admin and not c.user.can_create_subreddit:
            form.set_error(errors.CANT_CREATE_SR, "")
            c.errors.add(errors.CANT_CREATE_SR, field="")
            return

        if form.has_errors("ratelimit", errors.RATELIMIT):
            return

        VRatelimit.ratelimit(rate_user=True,
                             prefix="liveupdate_create_",
                             seconds=60)

        event = LiveUpdateEvent.new(
            id=None,
            title=title,
            description=description,
            resources=resources,
            banned=c.user._spam,
            nsfw=nsfw,
        )
        event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER)
        queries.create_event(event)

        amqp.add_item(
            "new_liveupdate_event",
            json.dumps({
                "event_fullname": event._fullname,
                "creator_fullname": c.user._fullname,
            }))

        form.redirect("/live/" + event._id)
        form._send_data(id=event._id)
        liveupdate_events.create_event(event, context=c, request=request)
    def POST_create(self, form, jquery, title, description):
        if not is_event_configuration_valid(form):
            return

        if form.has_errors("ratelimit", errors.RATELIMIT):
            return
        VRatelimit.ratelimit(
            rate_user=True, prefix="liveupdate_create_", seconds=60)

        event = LiveUpdateEvent.new(
            id=None,
            title=title,
            banned=c.user._spam,
        )
        event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER)
        queries.create_event(event)

        form.redirect("/live/" + event._id)
    def POST_create(self, form, jquery, title, description, resources, nsfw):
        """Create a new live thread.

        Once created, the initial settings can be modified with
        [/api/live/*thread*/edit](#POST_api_live_{thread}_edit) and new updates
        can be posted with
        [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """
        if not is_event_configuration_valid(form):
            return

        # for simplicity, set the live-thread creation threshold at the
        # subreddit creation threshold
        if not c.user_is_admin and not c.user.can_create_subreddit:
            form.set_error(errors.CANT_CREATE_SR, "")
            c.errors.add(errors.CANT_CREATE_SR, field="")
            return

        if form.has_errors("ratelimit", errors.RATELIMIT):
            return

        VRatelimit.ratelimit(
            rate_user=True, prefix="liveupdate_create_", seconds=60)

        event = LiveUpdateEvent.new(
            id=None,
            title=title,
            description=description,
            resources=resources,
            banned=c.user._spam,
            nsfw=nsfw,
        )
        event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER)
        queries.create_event(event)

        form.redirect("/live/" + event._id)
        form._send_data(id=event._id)
        liveupdate_events.create_event(event, context=c, request=request)
Exemplo n.º 10
0
    def POST_nominate(self, form, jquery, organization):
        if not feature.is_enabled('reddit_donate'):
            return self.abort404()

        if form.has_errors("organization", errors.DONATE_UNKNOWN_ORGANIZATION):
            return

        if form.has_errors("eligible", errors.DONATE_ACCOUNT_NOT_ELIGIBLE):
            return

        if form.has_errors("ratelimit", errors.RATELIMIT):
            return
        else:
            VRatelimit.ratelimit(
                rate_user=True,
                prefix="donate_nominate_",
                seconds=NOMINATION_COOLDOWN,
            )

        DonationNominationsByAccount.nominate(
            c.user,
            organization,
        )
Exemplo n.º 11
0
    def POST_nominate(self, form, jquery, organization):
        if not feature.is_enabled('reddit_donate'):
            return self.abort404()

        if form.has_errors("organization", errors.DONATE_UNKNOWN_ORGANIZATION):
            return

        if form.has_errors("eligible", errors.DONATE_ACCOUNT_NOT_ELIGIBLE):
            return

        if form.has_errors("ratelimit", errors.RATELIMIT):
            return
        else:
            VRatelimit.ratelimit(
                rate_user=True,
                prefix="donate_nominate_",
                seconds=NOMINATION_COOLDOWN,
            )

        DonationNominationsByAccount.nominate(
            c.user,
            organization,
        )
Exemplo n.º 12
0
    def POST_report_cache_poisoning(
        self,
        report_mac,
        poisoner_name,
        poisoner_id,
        poisoner_canary,
        victim_canary,
        render_time,
        route_name,
        url,
        source,
        cache_policy,
        resp_headers,
    ):
        """Report an instance of cache poisoning and its details"""

        self.OPTIONS_report_cache_poisoning()

        if c.errors:
            abort(400)

        # prevent simple CSRF by requiring a custom header
        if not request.headers.get('X-Loggit'):
            abort(403)

        # Eh? Why are you reporting this if the canaries are the same?
        if poisoner_canary == victim_canary:
            abort(400)

        expected_mac = make_poisoning_report_mac(
            poisoner_canary=poisoner_canary,
            poisoner_name=poisoner_name,
            poisoner_id=poisoner_id,
            cache_policy=cache_policy,
            source=source,
            route_name=route_name,
        )
        if not constant_time_compare(report_mac, expected_mac):
            abort(403)

        if resp_headers:
            try:
                resp_headers = json.loads(resp_headers)
                # Verify this is a JSON map of `header_name => [value, ...]`
                if not isinstance(resp_headers, dict):
                    abort(400)
                for hdr_name, hdr_vals in resp_headers.iteritems():
                    if not isinstance(hdr_name, basestring):
                        abort(400)
                    if not all(isinstance(h, basestring) for h in hdr_vals):
                        abort(400)
            except ValueError:
                abort(400)

        if not resp_headers:
            resp_headers = {}

        poison_info = dict(
            poisoner_name=poisoner_name,
            poisoner_id=str(poisoner_id),
            # Convert the JS timestamp to a standard one
            render_time=render_time * 1000,
            route_name=route_name,
            url=url,
            source=source,
            cache_policy=cache_policy,
            resp_headers=resp_headers,
        )

        # For immediate feedback when tracking the effects of caching changes
        g.stats.simple_event("cache.poisoning.%s.%s" % (source, cache_policy))
        # For longer-term diagnosing of caching issues
        g.events.cache_poisoning_event(poison_info, request=request, context=c)

        VRatelimit.ratelimit(rate_ip=True, prefix="rate_poison_", seconds=10)

        return self.api_wrapper({})
Exemplo n.º 13
0
    def POST_report_cache_poisoning(
            self,
            report_mac,
            poisoner_name,
            poisoner_id,
            poisoner_canary,
            victim_canary,
            render_time,
            route_name,
            url,
            source,
            cache_policy,
            resp_headers,
    ):
        """Report an instance of cache poisoning and its details"""

        self.OPTIONS_report_cache_poisoning()

        if c.errors:
            abort(400)

        # prevent simple CSRF by requiring a custom header
        if not request.headers.get('X-Loggit'):
            abort(403)

        # Eh? Why are you reporting this if the canaries are the same?
        if poisoner_canary == victim_canary:
            abort(400)

        expected_mac = make_poisoning_report_mac(
            poisoner_canary=poisoner_canary,
            poisoner_name=poisoner_name,
            poisoner_id=poisoner_id,
            cache_policy=cache_policy,
            source=source,
            route_name=route_name,
        )
        if not constant_time_compare(report_mac, expected_mac):
            abort(403)

        if resp_headers:
            try:
                resp_headers = json.loads(resp_headers)
                # Verify this is a JSON map of `header_name => [value, ...]`
                if not isinstance(resp_headers, dict):
                    abort(400)
                for hdr_name, hdr_vals in resp_headers.iteritems():
                    if not isinstance(hdr_name, basestring):
                        abort(400)
                    if not all(isinstance(h, basestring) for h in hdr_vals):
                        abort(400)
            except ValueError:
                abort(400)

        if not resp_headers:
            resp_headers = {}

        poison_info = dict(
            poisoner_name=poisoner_name,
            poisoner_id=str(poisoner_id),
            # Convert the JS timestamp to a standard one
            render_time=render_time * 1000,
            route_name=route_name,
            url=url,
            source=source,
            cache_policy=cache_policy,
            resp_headers=resp_headers,
        )

        # For immediate feedback when tracking the effects of caching changes
        g.stats.simple_event("cache.poisoning.%s.%s" % (source, cache_policy))
        # For longer-term diagnosing of caching issues
        g.events.cache_poisoning_event(poison_info, request=request, context=c)

        VRatelimit.ratelimit(rate_ip=True, prefix="rate_poison_", seconds=10)

        return self.api_wrapper({})
Exemplo n.º 14
0
def handle_register(
    controller, form, responder, name, email,
    password, rem=None, newsletter_subscribe=False,
    sponsor=False, signature=None, **kwargs
):

    def _event(error):
        g.events.login_event(
            'register_attempt',
            error_msg=error,
            user_name=request.urlvars.get('url_user'),
            email=request.POST.get('email'),
            remember_me=rem,
            newsletter=newsletter_subscribe,
            signature=signature,
            request=request,
            context=c)

    if signature and not signature.is_valid():
        _event(error="SIGNATURE")
        abort(403)

    if responder.has_errors('user', errors.USERNAME_TOO_SHORT):
        _event(error='USERNAME_TOO_SHORT')

    elif responder.has_errors('user', errors.USERNAME_INVALID_CHARACTERS):
        _event(error='USERNAME_INVALID_CHARACTERS')

    elif responder.has_errors('user', errors.USERNAME_TAKEN_DEL):
        _event(error='USERNAME_TAKEN_DEL')

    elif responder.has_errors('user', errors.USERNAME_TAKEN):
        _event(error='USERNAME_TAKEN')

    elif responder.has_errors('email', errors.BAD_EMAIL):
        _event(error='BAD_EMAIL')

    elif responder.has_errors('passwd', errors.SHORT_PASSWORD):
        _event(error='SHORT_PASSWORD')

    elif responder.has_errors('passwd', errors.BAD_PASSWORD):
        # BAD_PASSWORD is set when SHORT_PASSWORD is set
        _event(error='BAD_PASSWORD')

    elif responder.has_errors('passwd2', errors.BAD_PASSWORD_MATCH):
        _event(error='BAD_PASSWORD_MATCH')

    elif responder.has_errors('ratelimit', errors.RATELIMIT):
        _event(error='RATELIMIT')

    elif (not g.disable_captcha and
            responder.has_errors('captcha', errors.BAD_CAPTCHA)):
        _event(error='BAD_CAPTCHA')

    elif newsletter_subscribe and not email:
        c.errors.add(errors.NEWSLETTER_NO_EMAIL, field="email")
        form.has_errors("email", errors.NEWSLETTER_NO_EMAIL)
        _event(error='NEWSLETTER_NO_EMAIL')

    elif sponsor and not email:
        c.errors.add(errors.SPONSOR_NO_EMAIL, field="email")
        form.has_errors("email", errors.SPONSOR_NO_EMAIL)
        _event(error='SPONSOR_NO_EMAIL')

    else:
        try:
            user = register(name, password, request.ip)
        except AccountExists:
            c.errors.add(errors.USERNAME_TAKEN, field="user")
            form.has_errors("user", errors.USERNAME_TAKEN)
            _event(error='USERNAME_TAKEN')
            return

        VRatelimit.ratelimit(rate_ip=True, prefix="rate_register_")

        # anything else we know (email, languages)?
        if email:
            user.set_email(email)
            emailer.verify_email(user)

        user.pref_lang = c.lang
        user._commit()

        amqp.add_item('new_account', user._fullname)

        hooks.get_hook("account.registered").call(user=user)

        reject = hooks.get_hook("account.spotcheck").call(account=user)
        if any(reject):
            _event(error='ACCOUNT_SPOTCHECK')
            return

        if newsletter_subscribe and email:
            try:
                newsletter.add_subscriber(email, source="register")
            except newsletter.NewsletterError as e:
                g.log.warning("Failed to subscribe: %r" % e)

        controller._login(responder, user, rem)
        _event(error=None)
Exemplo n.º 15
0
def handle_register(controller,
                    form,
                    responder,
                    name,
                    email,
                    password,
                    rem=None,
                    newsletter_subscribe=False,
                    sponsor=False,
                    signature=None,
                    **kwargs):
    def _event(error):
        g.events.login_event('register_attempt',
                             error_msg=error,
                             user_name=request.urlvars.get('url_user'),
                             email=request.POST.get('email'),
                             remember_me=rem,
                             newsletter=newsletter_subscribe,
                             signature=signature,
                             request=request,
                             context=c)

    if signature and not signature.is_valid():
        _event(error="SIGNATURE")
        abort(403)

    if responder.has_errors('user', errors.USERNAME_TOO_SHORT):
        _event(error='USERNAME_TOO_SHORT')

    elif responder.has_errors('user', errors.USERNAME_INVALID_CHARACTERS):
        _event(error='USERNAME_INVALID_CHARACTERS')

    elif responder.has_errors('user', errors.USERNAME_TAKEN_DEL):
        _event(error='USERNAME_TAKEN_DEL')

    elif responder.has_errors('user', errors.USERNAME_TAKEN):
        _event(error='USERNAME_TAKEN')

    elif responder.has_errors('email', errors.BAD_EMAIL):
        _event(error='BAD_EMAIL')

    elif responder.has_errors('passwd', errors.SHORT_PASSWORD):
        _event(error='SHORT_PASSWORD')

    elif responder.has_errors('passwd', errors.BAD_PASSWORD):
        # BAD_PASSWORD is set when SHORT_PASSWORD is set
        _event(error='BAD_PASSWORD')

    elif responder.has_errors('passwd2', errors.BAD_PASSWORD_MATCH):
        _event(error='BAD_PASSWORD_MATCH')

    elif responder.has_errors('ratelimit', errors.RATELIMIT):
        _event(error='RATELIMIT')

    elif (not g.disable_captcha
          and responder.has_errors('captcha', errors.BAD_CAPTCHA)):
        _event(error='BAD_CAPTCHA')

    #Blockpath may require email
    elif not email and g.config.get('verifyemailrequired', ()):
        c.errors.add(errors.NO_EMAIL, field="email")
        form.has_errors("email", errors.NO_EMAIL)
        _event(
            error='NO_EMAIL')  #might already be set by ValidEmail validator??

    elif newsletter_subscribe and not email:
        c.errors.add(errors.NEWSLETTER_NO_EMAIL, field="email")
        form.has_errors("email", errors.NEWSLETTER_NO_EMAIL)
        _event(error='NEWSLETTER_NO_EMAIL')

    elif sponsor and not email:
        c.errors.add(errors.SPONSOR_NO_EMAIL, field="email")
        form.has_errors("email", errors.SPONSOR_NO_EMAIL)
        _event(error='SPONSOR_NO_EMAIL')

    else:
        try:
            user = register(name, password, request.ip)
        except AccountExists:
            c.errors.add(errors.USERNAME_TAKEN, field="user")
            form.has_errors("user", errors.USERNAME_TAKEN)
            _event(error='USERNAME_TAKEN')
            return

        VRatelimit.ratelimit(rate_ip=True,
                             prefix="rate_register_",
                             seconds=86400)

        # anything else we know (email, languages)?
        if email:
            user.set_email(email)
            emailer.verify_email(user)

        user.pref_lang = c.lang
        user._commit()

        amqp.add_item('new_account', user._fullname)

        hooks.get_hook("account.registered").call(user=user)

        reject = hooks.get_hook("account.spotcheck").call(account=user)
        if any(reject):
            _event(error='ACCOUNT_SPOTCHECK')
            return

        if newsletter_subscribe and email:
            try:
                newsletter.add_subscriber(email, source="register")
            except newsletter.NewsletterError as e:
                g.log.warning("Failed to subscribe: %r" % e)

        controller._login(responder, user, rem)
        _event(error=None)
Exemplo n.º 16
0
def handle_register(
    controller, form, responder, name, email,
    password, rem=None, newsletter_subscribe=False,
    sponsor=False, signature=None, **kwargs
):
    # check captcha before register (if any) since its answer might
    # change once c.user is set.
    captcha_shown = not signature and need_provider_captcha("register")

    def _event(error, captcha_shown=captcha_shown):
        g.events.login_event(
            'register_attempt',
            error_msg=error,
            user_name=request.urlvars.get('url_user'),
            email=request.POST.get('email'),
            remember_me=rem,
            newsletter=newsletter_subscribe,
            captcha_shown=captcha_shown,
            signature=signature,
            request=request,
            context=c)

    if signature and not signature.is_valid():
        _event(error="SIGNATURE")
        abort(403)

    if responder.has_errors('user', errors.USERNAME_TOO_SHORT):
        _event(error='USERNAME_TOO_SHORT')

    elif responder.has_errors('user', errors.USERNAME_INVALID_CHARACTERS):
        _event(error='USERNAME_INVALID_CHARACTERS')

    elif responder.has_errors('user', errors.USERNAME_TAKEN_DEL):
        _event(error='USERNAME_TAKEN_DEL')

    elif responder.has_errors('user', errors.USERNAME_TAKEN):
        _event(error='USERNAME_TAKEN')

    elif responder.has_errors('email', errors.BAD_EMAIL):
        _event(error='BAD_EMAIL')

    elif responder.has_errors('passwd', errors.SHORT_PASSWORD):
        _event(error='SHORT_PASSWORD')

    elif responder.has_errors('passwd', errors.BAD_PASSWORD):
        # BAD_PASSWORD is set when SHORT_PASSWORD is set
        _event(error='BAD_PASSWORD')

    elif responder.has_errors('passwd2', errors.BAD_PASSWORD_MATCH):
        _event(error='BAD_PASSWORD_MATCH')

    elif responder.has_errors('ratelimit', errors.RATELIMIT):
        _event(error='RATELIMIT')

    elif newsletter_subscribe and not email:
        c.errors.add(errors.NEWSLETTER_NO_EMAIL, field="email")
        form.has_errors("email", errors.NEWSLETTER_NO_EMAIL)
        _event(error='NEWSLETTER_NO_EMAIL')

    elif sponsor and not email:
        c.errors.add(errors.SPONSOR_NO_EMAIL, field="email")
        form.has_errors("email", errors.SPONSOR_NO_EMAIL)
        _event(error='SPONSOR_NO_EMAIL')

    # last but not least, we have to check the captcha
    elif (not signature and not g.disable_captcha and
          not valid_provider_captcha(responder, "register")):
        _event(error='BAD_CAPTCHA')

    else:
        try:
            user = register(name, password, request.ip)
        except AccountExists:
            c.errors.add(errors.USERNAME_TAKEN, field="user")
            form.has_errors("user", errors.USERNAME_TAKEN)
            _event(error='USERNAME_TAKEN')
            return

        VRatelimit.ratelimit(rate_ip=True, prefix="rate_register_")

        # anything else we know (email, languages)?
        if email:
            user.set_email(email)
            emailer.verify_email(user)

        user.pref_lang = c.lang

        if (is_api("html") and
                feature.is_enabled("new_user_onboarding", user=user)):
            user.has_been_onboarded = False

        user._commit()

        amqp.add_item('new_account', user._fullname)

        hooks.get_hook("account.registered").call(user=user)

        reject = hooks.get_hook("account.spotcheck").call(account=user)
        if any(reject):
            _event(error='ACCOUNT_SPOTCHECK')
            return

        if newsletter_subscribe and email:
            try:
                newsletter.add_subscriber(email, source="register")
            except newsletter.NewsletterError as e:
                g.log.warning("Failed to subscribe: %r" % e)

        controller._login(responder, user, rem)
        _event(error=None)