Exemple #1
0
def getUser(db, user_name):
    if user_name == configuration.base.SYSTEM_USER_NAME:
        return dbutils.User.makeSystem()
    try:
        return dbutils.User.fromName(db, user_name)
    except dbutils.NoSuchUser:
        if configuration.base.AUTHENTICATION_MODE == "host":
            email = getUserEmailAddress(user_name)
            return dbutils.User.create(
                db, user_name, user_name, email, email_verified=None)
        raise
Exemple #2
0
def getUser(db, user_name):
    if user_name == configuration.base.SYSTEM_USER_NAME:
        return user_name
    try:
        return dbutils.User.fromName(db, user_name)
    except dbutils.NoSuchUser:
        if configuration.base.AUTHENTICATION_MODE == "host":
            user = dbutils.User.create(db, user_name, user_name,
                                       getUserEmailAddress(user_name))
            db.commit()
            return user
        raise
Exemple #3
0
def process_request(environ, start_response):
    request_start = time.time()

    critic = api.critic.startSession()
    db = critic.database
    user = None

    try:
        try:
            req = request.Request(db, environ, start_response)
            req.setUser(db)

            if req.user is None:
                if configuration.base.AUTHENTICATION_MODE == "host":
                    user = dbutils.User.makeAnonymous()
                elif configuration.base.SESSION_TYPE == "httpauth":
                    req.requestHTTPAuthentication()
                    return []
                elif req.path.startswith("externalauth/"):
                    provider_name = req.path[len("externalauth/"):]
                    raise request.DoExternalAuthentication(provider_name)
                elif req.path.startswith("oauth/"):
                    provider_name = req.path[len("oauth/"):]
                    if provider_name in auth.PROVIDERS:
                        provider = auth.PROVIDERS[provider_name]
                        if isinstance(provider, auth.OAuthProvider):
                            if finishOAuth(db, req, provider):
                                return []
                elif configuration.base.SESSION_TYPE == "cookie":
                    if req.cookies.get("has_sid") == "1":
                        req.ensureSecure()
                    if configuration.base.ALLOW_ANONYMOUS_USER \
                            or req.path in request.INSECURE_PATHS \
                            or req.path.startswith("static-resource/"):
                        user = dbutils.User.makeAnonymous()
                    # Don't try to redirect POST requests to the login page.
                    elif req.method == "GET":
                        if configuration.base.AUTHENTICATION_MODE == "critic":
                            raise request.NeedLogin(req)
                        else:
                            raise request.DoExternalAuthentication(
                                configuration.base.AUTHENTICATION_MODE,
                                req.getTargetURL())
                if not user:
                    req.setStatus(403)
                    req.start()
                    return []
            else:
                try:
                    user = dbutils.User.fromName(db, req.user)
                except dbutils.NoSuchUser:
                    if configuration.base.AUTHENTICATION_MODE == "host":
                        email = getUserEmailAddress(req.user)
                        user = dbutils.User.create(db,
                                                   req.user,
                                                   req.user,
                                                   email,
                                                   email_verified=None)
                        db.commit()
                    else:
                        # This can't really happen.
                        raise

            if not user.isAnonymous():
                critic.setActualUser(api.user.fetch(critic, user_id=user.id))

            user.loadPreferences(db)

            if user.status == 'retired':
                cursor = db.cursor()
                cursor.execute("UPDATE users SET status='current' WHERE id=%s",
                               (user.id, ))
                user = dbutils.User.fromId(db, user.id)
                db.commit()

            if not user.getPreference(db, "debug.profiling.databaseQueries"):
                db.disableProfiling()

            if not req.path:
                if user.isAnonymous():
                    location = "tutorial"
                else:
                    location = user.getPreference(db, "defaultPage")

                if req.query:
                    location += "?" + req.query

                req.setStatus(307)
                req.addResponseHeader("Location", location)
                req.start()
                return []

            if req.path == "redirect":
                target = req.getParameter("target", "/")

                if req.method == "POST":
                    # Don't use HTTP redirect for POST requests.

                    req.setContentType("text/html")
                    req.start()

                    return [
                        "<meta http-equiv='refresh' content='0; %s'>" %
                        htmlify(target)
                    ]
                else:
                    raise request.MovedTemporarily(target)

            # Require a .git suffix on HTTP(S) repository URLs unless the user-
            # agent starts with "git/" (as Git's normally does.)
            #
            # Our objectives are:
            #
            # 1) Not to require Git's user-agent to be its default value, since
            #    the user might have to override it to get through firewalls.
            # 2) Never to send regular user requests to 'git http-backend' by
            #    mistake.
            #
            # This is a compromise.

            if req.getRequestHeader("User-Agent", "").startswith("git/"):
                suffix = None
            else:
                suffix = ".git"

            if handleRepositoryPath(db, req, user, suffix):
                db = None
                return []

            if req.path.startswith("!/"):
                req.path = req.path[2:]
            elif configuration.extensions.ENABLED:
                handled = extensions.role.page.execute(db, req, user)
                if isinstance(handled, basestring):
                    req.start()
                    return [handled]

            if req.path.startswith("static-resource/"):
                return handleStaticResource(req)

            if req.path.startswith("r/"):
                req.updateQuery({"id": [req.path[2:]]})
                req.path = "showreview"

            if configuration.extensions.ENABLED:
                match = RE_EXTENSION_RESOURCE.match(req.path)
                if match:
                    content_type, resource = extensions.resource.get(
                        req, db, user, match.group(1))
                    if resource:
                        req.setContentType(content_type)
                        if content_type.startswith("image/"):
                            req.addResponseHeader("Cache-Control",
                                                  "max-age=3600")
                        req.start()
                        return [resource]
                    else:
                        req.setStatus(404)
                        req.start()
                        return []

            if req.path.startswith("download/"):
                operationfn = download
            elif req.path == "api" or req.path.startswith("api/"):
                try:
                    result = jsonapi.handle(critic, req)
                except jsonapi.Error as error:
                    req.setStatus(error.http_status)
                    result = {
                        "error": {
                            "title": error.title,
                            "message": error.message
                        }
                    }
                else:
                    req.setStatus(200)

                accept_header = req.getRequestHeader("Accept")
                if accept_header == "application/vnd.api+json":
                    default_indent = None
                else:
                    default_indent = 2
                indent = req.getParameter("indent", default_indent, filter=int)
                if indent == 0:
                    # json.encode(..., indent=0) still gives line-breaks, just
                    # no indentation.  This is not so useful, so set indent to
                    # None instead, which disables formatting entirely.
                    indent = None

                req.setContentType("application/vnd.api+json")
                req.start()
                return [json_encode(result, indent=indent)]
            else:
                operationfn = OPERATIONS.get(req.path)

            if operationfn:
                result = operationfn(req, db, user)

                if isinstance(result, (OperationResult, OperationError)):
                    req.setContentType("text/json")

                    if isinstance(result, OperationResult):
                        if db.profiling:
                            result.set("__profiling__", formatDBProfiling(db))
                            result.set("__time__", time.time() - request_start)
                elif not req.hasContentType():
                    req.setContentType("text/plain")

                req.start()

                if isinstance(result, unicode):
                    return [result.encode("utf8")]
                else:
                    return [str(result)]

            override_user = req.getParameter("user", None)

            while True:
                pagefn = PAGES.get(req.path)
                if pagefn:
                    try:
                        if not user.isAnonymous() and override_user:
                            user = dbutils.User.fromName(db, override_user)

                        req.setContentType("text/html")

                        result = pagefn(req, db, user)

                        if db.profiling and not (isinstance(result, str) or
                                                 isinstance(result, Document)):
                            source = ""
                            for fragment in result:
                                source += fragment
                            result = source

                        if isinstance(result, str) or isinstance(
                                result, Document):
                            req.start()
                            result = str(result)
                            result += ("<!-- total request time: %.2f ms -->" %
                                       ((time.time() - request_start) * 1000))
                            if db.profiling:
                                result += ("<!--\n\n%s\n\n -->" %
                                           formatDBProfiling(db))
                            return [result]
                        else:
                            result = WrappedResult(db, req, user, result)
                            req.start()

                            # Prevent the finally clause below from closing the
                            # connection.  WrappedResult does it instead.
                            db = None

                            return result
                    except gitutils.NoSuchRepository as error:
                        raise page.utils.DisplayMessage(
                            title="Invalid URI Parameter!", body=error.message)
                    except gitutils.GitReferenceError as error:
                        if error.ref:
                            raise page.utils.DisplayMessage(
                                title="Specified ref not found",
                                body=("There is no ref named \"%s\" in %s." %
                                      (error.ref, error.repository)))
                        elif error.sha1:
                            raise page.utils.DisplayMessage(
                                title="SHA-1 not found", body=error.message)
                        else:
                            raise
                    except dbutils.NoSuchUser as error:
                        raise page.utils.DisplayMessage(
                            title="Invalid URI Parameter!", body=error.message)
                    except dbutils.NoSuchReview as error:
                        raise page.utils.DisplayMessage(
                            title="Invalid URI Parameter!", body=error.message)

                path = req.path

                if "/" in path:
                    repository = gitutils.Repository.fromName(
                        db,
                        path.split("/", 1)[0])
                    if repository: path = path.split("/", 1)[1]
                else:
                    repository = None

                def revparsePlain(item):
                    try:
                        return gitutils.getTaggedCommit(
                            repository, repository.revparse(item))
                    except:
                        raise

                revparse = revparsePlain

                if repository is None:
                    review_id = req.getParameter("review", None, filter=int)

                    if review_id:
                        cursor = db.cursor()
                        cursor.execute(
                            """SELECT repository
                                            FROM branches
                                            JOIN reviews ON (reviews.branch=branches.id)
                                           WHERE reviews.id=%s""",
                            (review_id, ))
                        row = cursor.fetchone()
                        if row:
                            repository = gitutils.Repository.fromId(db, row[0])

                            def revparseWithReview(item):
                                if re.match("^[0-9a-f]+$", item):
                                    cursor.execute(
                                        """SELECT sha1
                                                        FROM commits
                                                        JOIN changesets ON (changesets.child=commits.id)
                                                        JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id)
                                                       WHERE reviewchangesets.review=%s
                                                         AND commits.sha1 LIKE %s""",
                                        (review_id, item + "%"))
                                    row = cursor.fetchone()
                                    if row: return row[0]
                                    else: return revparsePlain(item)

                            revparse = revparseWithReview

                if repository is None:
                    repository = gitutils.Repository.fromName(
                        db, user.getPreference(db, "defaultRepository"))

                    if gitutils.re_sha1.match(path):
                        if repository and not repository.iscommit(path):
                            repository = None

                        if not repository:
                            try:
                                repository = gitutils.Repository.fromSHA1(
                                    db, path)
                            except gitutils.GitReferenceError:
                                repository = None

                if repository:
                    try:
                        items = filter(None, map(revparse, path.split("..")))
                        updated_query = {}

                        if len(items) == 1:
                            updated_query["repository"] = [repository.name]
                            updated_query["sha1"] = [items[0]]
                        elif len(items) == 2:
                            updated_query["repository"] = [repository.name]
                            updated_query["from"] = [items[0]]
                            updated_query["to"] = [items[1]]

                        if updated_query:
                            req.updateQuery(updated_query)
                            req.path = "showcommit"
                            continue
                    except gitutils.GitReferenceError:
                        pass

                break

            raise page.utils.DisplayMessage(title="Not found!",
                                            body="Page not handled: /%s" %
                                            path,
                                            status=404)
        except GeneratorExit:
            raise
        except page.utils.NotModified:
            req.setStatus(304)
            req.start()
            return []
        except request.MovedTemporarily as redirect:
            req.setStatus(307)
            req.addResponseHeader("Location", redirect.location)
            if redirect.no_cache:
                req.addResponseHeader("Cache-Control", "no-cache")
            req.start()
            return []
        except request.DoExternalAuthentication as command:
            command.execute(db, req)
            return []
        except request.MissingWSGIRemoteUser as error:
            # req object is not initialized yet.
            start_response("200 OK", [("Content-Type", "text/html")])
            return [
                """\
<pre>error: Critic was configured with '--auth-mode host' but there was no
REMOTE_USER variable in the WSGI environ dict provided by the web server.

To fix this you can either reinstall Critic using '--auth-mode critic' (to let
Critic handle user authentication automatically), or you can configure user
authentication properly in the web server.  For apache2, the latter can be done
by adding the something like the following to the apache site configuration for
Critic:

        &lt;Location /&gt;
                AuthType Basic
                AuthName "Authentication Required"
                AuthUserFile "/path/to/critic-main.htpasswd.users"
                Require valid-user
        &lt;/Location&gt;

If you need more dynamic http authentication you can instead setup mod_wsgi with
a custom WSGIAuthUserScript directive.  This will cause the provided credentials
to be passed to a Python function called check_password() that you can implement
yourself.  This way you can validate the user/pass via any existing database or
for example an LDAP server.  For more information on setting up such
authentication in apache2, see:

  <a href="%(url)s">%(url)s</a></pre>""" % {
                    "url":
                    "http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Authentication_Provider"
                }
            ]
        except page.utils.DisplayMessage as message:
            if user is None:
                user = dbutils.User.makeAnonymous()

            document = page.utils.displayMessage(db,
                                                 req,
                                                 user,
                                                 title=message.title,
                                                 message=message.body,
                                                 review=message.review,
                                                 is_html=message.html)

            req.setContentType("text/html")
            req.setStatus(message.status)
            req.start()

            return [str(document)]
        except page.utils.DisplayFormattedText as formatted_text:
            if user is None:
                user = dbutils.User.makeAnonymous()

            document = page.utils.displayFormattedText(db, req, user,
                                                       formatted_text.source)

            req.setContentType("text/html")
            req.start()

            return [str(document)]
        except Exception:
            # crash might be psycopg2.ProgrammingError so rollback to avoid
            # "InternalError: current transaction is aborted" inside handleException()
            if db and db.closed():
                db = None
            elif db:
                db.rollback()

            error_title, error_body = handleException(db, req, user)
            error_body = reflow("\n\n".join(error_body))
            error_message = "\n".join(
                [error_title, "=" * len(error_title), "", error_body])

            assert not req.isStarted()

            req.setStatus(500)
            req.setContentType("text/plain")
            req.start()

            return [error_message]
    finally:
        if db:
            db.close()
Exemple #4
0
def process_request(environ, start_response):
    request_start = time.time()

    db = dbutils.Database()
    user = None

    try:
        try:
            req = request.Request(db, environ, start_response)
            req.setUser(db)

            if req.user is None:
                if configuration.base.AUTHENTICATION_MODE == "host":
                    user = dbutils.User.makeAnonymous()
                elif configuration.base.SESSION_TYPE == "httpauth":
                    req.requestHTTPAuthentication()
                    return []
                elif req.path.startswith("externalauth/"):
                    provider_name = req.path[len("externalauth/"):]
                    raise request.DoExternalAuthentication(provider_name)
                elif req.path.startswith("oauth/"):
                    provider_name = req.path[len("oauth/"):]
                    if provider_name in auth.PROVIDERS:
                        provider = auth.PROVIDERS[provider_name]
                        if isinstance(provider, auth.OAuthProvider):
                            if finishOAuth(db, req, provider):
                                return []
                elif configuration.base.SESSION_TYPE == "cookie":
                    if req.cookies.get("has_sid") == "1":
                        req.ensureSecure()
                    if configuration.base.ALLOW_ANONYMOUS_USER \
                            or req.path in request.INSECURE_PATHS \
                            or req.path.startswith("static-resource/"):
                        user = dbutils.User.makeAnonymous()
                    # Don't try to redirect POST requests to the login page.
                    elif req.method == "GET":
                        if configuration.base.AUTHENTICATION_MODE == "critic":
                            raise request.NeedLogin(req)
                        else:
                            raise request.DoExternalAuthentication(
                                configuration.base.AUTHENTICATION_MODE,
                                req.getTargetURL())
                if not user:
                    req.setStatus(403)
                    req.start()
                    return []
            else:
                try:
                    user = dbutils.User.fromName(db, req.user)
                except dbutils.NoSuchUser:
                    if configuration.base.AUTHENTICATION_MODE == "host":
                        email = getUserEmailAddress(req.user)
                        user = dbutils.User.create(
                            db, req.user, req.user, email, email_verified=None)
                        db.commit()
                    else:
                        # This can't really happen.
                        raise

            user.loadPreferences(db)

            if user.status == 'retired':
                cursor = db.cursor()
                cursor.execute("UPDATE users SET status='current' WHERE id=%s", (user.id,))
                user = dbutils.User.fromId(db, user.id)
                db.commit()

            if not user.getPreference(db, "debug.profiling.databaseQueries"):
                db.disableProfiling()

            if not req.path:
                if user.isAnonymous():
                    location = "tutorial"
                else:
                    location = user.getPreference(db, "defaultPage")

                if req.query:
                    location += "?" + req.query

                req.setStatus(307)
                req.addResponseHeader("Location", location)
                req.start()
                return []

            if req.path == "redirect":
                target = req.getParameter("target", "/")

                if req.method == "POST":
                    # Don't use HTTP redirect for POST requests.

                    req.setContentType("text/html")
                    req.start()

                    return ["<meta http-equiv='refresh' content='0; %s'>" % htmlify(target)]
                else:
                    raise request.MovedTemporarily(target)

            # Require a .git suffix on HTTP(S) repository URLs unless the user-
            # agent starts with "git/" (as Git's normally does.)
            #
            # Our objectives are:
            #
            # 1) Not to require Git's user-agent to be its default value, since
            #    the user might have to override it to get through firewalls.
            # 2) Never to send regular user requests to 'git http-backend' by
            #    mistake.
            #
            # This is a compromise.

            if req.getRequestHeader("User-Agent", "").startswith("git/"):
                suffix = None
            else:
                suffix = ".git"

            if handleRepositoryPath(db, req, user, suffix):
                db = None
                return []

            if req.path.startswith("!/"):
                req.path = req.path[2:]
            elif configuration.extensions.ENABLED:
                handled = extensions.role.page.execute(db, req, user)
                if isinstance(handled, basestring):
                    req.start()
                    return [handled]

            if req.path.startswith("static-resource/"):
                return handleStaticResource(req)

            if req.path.startswith("r/"):
                req.query = "id=" + req.path[2:] + ("&" + req.query if req.query else "")
                req.path = "showreview"

            if configuration.extensions.ENABLED:
                match = RE_EXTENSION_RESOURCE.match(req.path)
                if match:
                    content_type, resource = extensions.resource.get(req, db, user, match.group(1))
                    if resource:
                        req.setContentType(content_type)
                        if content_type.startswith("image/"):
                            req.addResponseHeader("Cache-Control", "max-age=3600")
                        req.start()
                        return [resource]
                    else:
                        req.setStatus(404)
                        req.start()
                        return []

            if req.path.startswith("download/"):
                operationfn = download
            else:
                operationfn = OPERATIONS.get(req.path)

            if operationfn:
                result = operationfn(req, db, user)

                if isinstance(result, (OperationResult, OperationError)):
                    req.setContentType("text/json")

                    if isinstance(result, OperationResult):
                        if db.profiling:
                            result.set("__profiling__", formatDBProfiling(db))
                            result.set("__time__", time.time() - request_start)
                elif not req.hasContentType():
                    req.setContentType("text/plain")

                req.start()

                if isinstance(result, unicode):
                    return [result.encode("utf8")]
                else:
                    return [str(result)]

            override_user = req.getParameter("user", None)

            while True:
                pagefn = PAGES.get(req.path)
                if pagefn:
                    try:
                        if not user.isAnonymous() and override_user:
                            user = dbutils.User.fromName(db, override_user)

                        req.setContentType("text/html")

                        result = pagefn(req, db, user)

                        if db.profiling and not (isinstance(result, str) or
                                                 isinstance(result, Document)):
                            source = ""
                            for fragment in result:
                                source += fragment
                            result = source

                        if isinstance(result, str) or isinstance(result, Document):
                            req.start()
                            result = str(result)
                            result += ("<!-- total request time: %.2f ms -->"
                                       % ((time.time() - request_start) * 1000))
                            if db.profiling:
                                result += ("<!--\n\n%s\n\n -->"
                                           % formatDBProfiling(db))
                            return [result]
                        else:
                            result = WrappedResult(db, req, user, result)
                            req.start()

                            # Prevent the finally clause below from closing the
                            # connection.  WrappedResult does it instead.
                            db = None

                            return result
                    except gitutils.NoSuchRepository as error:
                        raise page.utils.DisplayMessage(
                            title="Invalid URI Parameter!",
                            body=error.message)
                    except gitutils.GitReferenceError as error:
                        if error.ref:
                            raise page.utils.DisplayMessage(
                                title="Specified ref not found",
                                body=("There is no ref named \"%s\" in %s."
                                      % (error.ref, error.repository)))
                        elif error.sha1:
                            raise page.utils.DisplayMessage(
                                title="SHA-1 not found",
                                body=error.message)
                        else:
                            raise
                    except dbutils.NoSuchUser as error:
                        raise page.utils.DisplayMessage(
                            title="Invalid URI Parameter!",
                            body=error.message)
                    except dbutils.NoSuchReview as error:
                        raise page.utils.DisplayMessage(
                            title="Invalid URI Parameter!",
                            body=error.message)

                path = req.path

                if "/" in path:
                    repository = gitutils.Repository.fromName(db, path.split("/", 1)[0])
                    if repository: path = path.split("/", 1)[1]
                else:
                    repository = None

                def revparsePlain(item):
                    try: return gitutils.getTaggedCommit(repository, repository.revparse(item))
                    except: raise
                revparse = revparsePlain

                if repository is None:
                    review_id = req.getParameter("review", None, filter=int)

                    if review_id:
                        cursor = db.cursor()
                        cursor.execute("""SELECT repository
                                            FROM branches
                                            JOIN reviews ON (reviews.branch=branches.id)
                                           WHERE reviews.id=%s""",
                                       (review_id,))
                        row = cursor.fetchone()
                        if row:
                            repository = gitutils.Repository.fromId(db, row[0])
                            def revparseWithReview(item):
                                if re.match("^[0-9a-f]+$", item):
                                    cursor.execute("""SELECT sha1
                                                        FROM commits
                                                        JOIN changesets ON (changesets.child=commits.id)
                                                        JOIN reviewchangesets ON (reviewchangesets.changeset=changesets.id)
                                                       WHERE reviewchangesets.review=%s
                                                         AND commits.sha1 LIKE %s""",
                                                   (review_id, item + "%"))
                                    row = cursor.fetchone()
                                    if row: return row[0]
                                    else: return revparsePlain(item)
                            revparse = revparseWithReview

                if repository is None:
                    repository = gitutils.Repository.fromName(
                        db, user.getPreference(db, "defaultRepository"))

                    if gitutils.re_sha1.match(path):
                        if repository and not repository.iscommit(path):
                            repository = None

                        if not repository:
                            try:
                                repository = gitutils.Repository.fromSHA1(db, path)
                            except gitutils.GitReferenceError:
                                repository = None

                if repository:
                    try:
                        items = filter(None, map(revparse, path.split("..")))
                        query = None

                        if len(items) == 1:
                            query = ("repository=%d&sha1=%s"
                                     % (repository.id, items[0]))
                        elif len(items) == 2:
                            query = ("repository=%d&from=%s&to=%s"
                                     % (repository.id, items[0], items[1]))

                        if query:
                            if req.query:
                                query += "&" + req.query

                            req.query = query
                            req.path = "showcommit"
                            continue
                    except gitutils.GitReferenceError:
                        pass

                break

            req.setStatus(404)
            raise page.utils.DisplayMessage(
                title="Not found!",
                body="Page not handled: /%s" % path)
        except GeneratorExit:
            raise
        except page.utils.NotModified:
            req.setStatus(304)
            req.start()
            return []
        except request.MovedTemporarily as redirect:
            req.setStatus(307)
            req.addResponseHeader("Location", redirect.location)
            if redirect.no_cache:
                req.addResponseHeader("Cache-Control", "no-cache")
            req.start()
            return []
        except request.DoExternalAuthentication as command:
            command.execute(db, req)
            return []
        except request.MissingWSGIRemoteUser as error:
            # req object is not initialized yet.
            start_response("200 OK", [("Content-Type", "text/html")])
            return ["""\
<pre>error: Critic was configured with '--auth-mode host' but there was no
REMOTE_USER variable in the WSGI environ dict provided by the web server.

To fix this you can either reinstall Critic using '--auth-mode critic' (to let
Critic handle user authentication automatically), or you can configure user
authentication properly in the web server.  For apache2, the latter can be done
by adding the something like the following to the apache site configuration for
Critic:

        &lt;Location /&gt;
                AuthType Basic
                AuthName "Authentication Required"
                AuthUserFile "/path/to/critic-main.htpasswd.users"
                Require valid-user
        &lt;/Location&gt;

If you need more dynamic http authentication you can instead setup mod_wsgi with
a custom WSGIAuthUserScript directive.  This will cause the provided credentials
to be passed to a Python function called check_password() that you can implement
yourself.  This way you can validate the user/pass via any existing database or
for example an LDAP server.  For more information on setting up such
authentication in apache2, see:

  <a href="%(url)s">%(url)s</a></pre>""" % { "url": "http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms#Apache_Authentication_Provider" }]
        except page.utils.DisplayMessage as message:
            if user is None:
                user = dbutils.User.makeAnonymous()

            document = page.utils.displayMessage(
                db, req, user, title=message.title, message=message.body,
                review=message.review, is_html=message.html)

            req.setContentType("text/html")
            req.start()

            return [str(document)]
        except Exception:
            # crash might be psycopg2.ProgrammingError so rollback to avoid
            # "InternalError: current transaction is aborted" inside handleException()
            if db and db.closed():
                db = None
            elif db:
                db.rollback()

            error_title, error_body = handleException(db, req, user)
            error_body = reflow("\n\n".join(error_body))
            error_message = "\n".join([error_title,
                                       "=" * len(error_title),
                                       "",
                                       error_body])

            assert not req.isStarted()

            req.setStatus(500)
            req.setContentType("text/plain")
            req.start()

            return [error_message]
    finally:
        if db:
            db.close()
Exemple #5
0
def checkSession(db, req):
    """Check if the request is part of a session and if so set req.user

       Raises an request.HTTPResponse exception if immediate action is required,
       otherwise sets req.user to non-None (but possibly to the anonymous user)
       and returns."""

    # Step 1: If the host web server is supposed to authenticate users, use the
    #         $REMOTE_USER environment variable.
    if configuration.base.AUTHENTICATION_MODE == "host":
        # Strip white-space, since Apache is known to do this internally when
        # authenticating, but then passing on the original unstripped string to
        # us on success.
        username = req.getEnvironment().get("REMOTE_USER", "").strip()
        if not username:
            # No REMOTE_USER variable.  If we support anonymous users, this is
            # fine, otherwise it indicates a configuration error.
            if configuration.base.ALLOW_ANONYMOUS_USER:
                db.setUser(dbutils.User.makeAnonymous())
                return
            raise request.MissingWSGIRemoteUser()

        # We have a username.  Fetch the (or create a) matching user record.
        try:
            db.setUser(dbutils.User.fromName(db, username))
        except dbutils.NoSuchUser:
            email = getUserEmailAddress(username)
            db.setUser(dbutils.User.create(
                db, username, username, email, email_verified=None))
        return

    # Step 2: If cookie based sessions are used, check if there is a valid
    #         session cookie.
    if configuration.base.SESSION_TYPE == "cookie":
        sid = req.cookies.get("sid")
        if sid:
            cursor = db.cursor()
            cursor.execute(
                """SELECT uid, labels, EXTRACT('epoch' FROM NOW() - atime) AS age
                     FROM usersessions
                    WHERE key=%s""",
                (sid,))

            row = cursor.fetchone()
            if row:
                user_id, labels, session_age = row

                if configuration.base.SESSION_MAX_AGE == 0 \
                        or session_age < configuration.base.SESSION_MAX_AGE:
                    # This is a valid session cookie.
                    user = dbutils.User.fromId(db, user_id)
                    if labels is None:
                        labels = auth.DATABASE.getAuthenticationLabels(user)
                    else:
                        labels = labels.split("|") if labels else ()
                    db.setUser(user, labels)
                    return

                # The session cookie is too old.  Delete it from the database.
                with db.updating_cursor("usersessions") as cursor:
                    cursor.execute("""DELETE FROM usersessions
                                            WHERE key=%s""",
                                   (sid,))

            # The session cookie is not valid.  Delete it from the browser.
            req.deleteCookie("sid")
            # Also delete the has_sid cookie, if there is one.
            req.deleteCookie("has_sid")

            # Since the session seems to have expired, offer the user to sign in
            # again by redirecting to the login page.  Signing in is optional
            # though, meaning the login page will have a "Continue anonymously"
            # link (if anonymous access is allowed.)
            #
            # Exception: Don't do this if /login is being requested.
            if req.allowRedirect(307) and req.path != "login":
                raise request.NeedLogin(req, optional=True)

        elif req.cookies.get("has_sid") == "1":
            # The request had no session cookie, but had the has_sid cookie that
            # indicates the browser ought to have a sesssion cookie.  Typically,
            # this means a signed in user accesses a mixed HTTP/HTTPS system
            # over HTTP.  If so, redirect the user to HTTPS.
            req.ensureSecure()

            # The above call would have raised if a redirect was meaningful.  If
            # it didn't, the has_sid cookie is bogus, so delete it.
            req.deleteCookie("has_sid")

        elif req.cookies.get("has_sid") == "0":
            # This indicates that the user just signed out.  If anonymous access
            # is not allowed, we'll redirect the user to the login page again,
            # which is sort of a bit unhelpful.
            #
            # Worse yet; if use of an external authentication provider is
            # enforced, the login page will redirect there, which might sign the
            # user back in, non-interactively.  In that case, signing out would
            # be impossible.
            #
            # So, instead, detect the sign-out and return a simple "you have
            # signed out" page in this case.

            # Delete the cookie.  This means that on reload, the user is
            # redirected to the login page again.  (This is to prevent the user
            # from getting stuck on this "you have signed out" page.)
            req.deleteCookie("has_sid")

            # Do the redirect if anonymous access isn't allowed.  Also don't do
            # it on the actual login page.
            if not configuration.base.ALLOW_ANONYMOUS_USER \
                    and req.path != "login":
                raise request.DisplayMessage(
                    title="You have signed out",
                    body="To use this system, you will need to sign in again.")

    # Step 3(a): Check if there's a valid HTTP Authorization header (even if
    #            cookie based sessions are typically used.)  If there is such a
    #            header, we assume HTTP authentication was meant to be used, and
    #            respond with a 401 Unauthorized response if authentication
    #            using the header fails.
    authorization_header = req.getRequestHeader("Authorization")
    if authorization_header:
        import base64

        try:
            authtype, base64_credentials = authorization_header.split(None, 1)
        except ValueError:
            authtype = "invalid"
        if authtype.lower() != "basic":
            raise request.RequestHTTPAuthentication()

        try:
            credentials = base64.b64decode(base64_credentials)
        except (ValueError, TypeError) as error:
            raise request.RequestHTTPAuthentication()

        username, _, password = credentials.partition(":")
        username = username.strip()

        if username and password:
            try:
                auth.DATABASE.performHTTPAuthentication(db, username, password)
                req.session_type = "httpauth"
                return
            except auth.AuthenticationFailed:
                pass

        raise request.RequestHTTPAuthentication()

    # Step 3(b): If the request has a "use_httpauth" cookie, request/require
    #            HTTP authentication.  This is a just a convenience feature for
    #            clients using HTTP stacks that only send credentials in
    #            response to server challenges.  (If cookie sessions are used,
    #            no such challenge would normally be returned, we'd rather
    #            redirect to the login page.)
    if req.cookies.get("use_httpauth"):
        raise request.RequestHTTPAuthentication()
    # Also do this for requests with a "httpauth=yes" query parameter.
    if req.getParameter("httpauth", "no") == "yes":
        raise request.RequestHTTPAuthentication()

    # Step 4: If anonymous access is supported or if it should be allowed as an
    #         exception for the accessed path, leave the session anonymous.
    if configuration.base.ALLOW_ANONYMOUS_USER or isInsecurePath(req):
        db.setUser(dbutils.User.makeAnonymous())
        req.session_type = None
        return

    # Step 5: If HTTP authentication is required (i.e. no session cookies) then
    #         request that now.
    if configuration.base.SESSION_TYPE == "httpauth":
        raise request.RequestHTTPAuthentication()

    # Step 6: Cookie based sessions are enabled, and not anonymous access.  If
    #         this is a POST or PUT request, respond with 403 Forbidden, and
    #         otherwise redirect to the login page.
    if not req.allowRedirect(307):
        raise request.Forbidden("Valid user session required")

    raise request.NeedLogin(req, optional=req.cookies.has_key("has_sid"))
Exemple #6
0
def main(environ, start_response):
    request_start = time.time()

    db = dbutils.Database()
    user = None

    try:
        try:
            req = request.Request(db, environ, start_response)

            if req.user is None:
                if configuration.base.AUTHENTICATION_MODE == "critic":
                    if configuration.base.SESSION_TYPE == "httpauth":
                        req.setStatus(401)
                        req.addResponseHeader("WWW-Authenticate",
                                              "Basic realm=\"Critic\"")
                        req.start()
                        return
                    elif configuration.base.ALLOW_ANONYMOUS_USER or req.path in (
                            "login", "validatelogin"):
                        user = dbutils.User.makeAnonymous()
                    elif req.method == "GET":
                        raise page.utils.NeedLogin, req
                    else:
                        # Don't try to redirect POST requests to the login page.
                        req.setStatus(403)
                        req.start()
                        return
            else:
                try:
                    user = dbutils.User.fromName(db, req.user)
                except dbutils.NoSuchUser:
                    cursor = db.cursor()
                    cursor.execute(
                        """INSERT INTO users (name, email, fullname)
                                           VALUES (%s, %s, %s)
                                        RETURNING id""",
                        (req.user, getUserEmailAddress(req.user), req.user))
                    user = dbutils.User.fromId(db, cursor.fetchone()[0])
                    db.commit()

            user.loadPreferences(db)

            if user.status == 'retired':
                cursor = db.cursor()
                cursor.execute("UPDATE users SET status='current' WHERE id=%s",
                               (user.id, ))
                user = dbutils.User.fromId(db, user.id)
                db.commit()

            if not user.getPreference(db, "debug.profiling.databaseQueries"):
                db.disableProfiling()

            if not req.path:
                if user.isAnonymous():
                    location = "tutorial"
                else:
                    location = user.getPreference(db, "defaultPage")

                if req.query:
                    location += "?" + req.query

                req.setStatus(307)
                req.addResponseHeader("Location", location)
                req.start()
                return

            if req.path == "redirect":
                target = req.getParameter("target", "/")

                if req.method == "POST":
                    # Don't use HTTP redirect for POST requests.

                    req.setContentType("text/html")
                    req.start()

                    yield "<meta http-equiv='refresh' content='0; %s'>" % htmlify(
                        target)
                    return
                else:
                    raise page.utils.MovedTemporarily, target

            if req.path.startswith("!/"):
                req.path = req.path[2:]
            elif configuration.extensions.ENABLED:
                handled = extensions.executePage(db, req, user)
                if handled:
                    req.start()
                    yield handled
                    return

            if req.path.startswith("r/"):
                req.query = "id=" + req.path[2:] + ("&" + req.query
                                                    if req.query else "")
                req.path = "showreview"

            if configuration.extensions.ENABLED:
                match = RE_EXTENSION_RESOURCE.match(req.path)
                if match:
                    content_type, resource = extensions.getExtensionResource(
                        req, db, user, match.group(1))
                    if resource:
                        req.setContentType(content_type)
                        req.start()
                        yield resource
                        return
                    else:
                        req.setStatus(404)
                        req.start()
                        return

            if req.path.startswith("download/"): operation = download
            else: operation = operations.get(req.path)
            if operation:
                req.setContentType("text/plain")

                try:
                    result = operation(req, db, user)
                except OperationError, error:
                    result = error
                except page.utils.DisplayMessage, message:
                    result = "error:" + message.title
                    if message.body: result += "  " + message.body
                except Exception, exception:
                    result = "error:\n" + "".join(
                        traceback.format_exception(*sys.exc_info()))
Exemple #7
0
def checkSession(db, req):
    """Check if the request is part of a session and if so set req.user

       Raises an request.HTTPResponse exception if immediate action is required,
       otherwise sets req.user to non-None (but possibly to the anonymous user)
       and returns."""

    # Step 1: If the host web server is supposed to authenticate users, use the
    #         $REMOTE_USER environment variable.
    if configuration.base.AUTHENTICATION_MODE == "host":
        # Strip white-space, since Apache is known to do this internally when
        # authenticating, but then passing on the original unstripped string to
        # us on success.
        username = req.getEnvironment().get("REMOTE_USER", "").strip()
        if not username:
            # No REMOTE_USER variable.  If we support anonymous users, this is
            # fine, otherwise it indicates a configuration error.
            if configuration.base.ALLOW_ANONYMOUS_USER:
                db.setUser(dbutils.User.makeAnonymous())
                return
            raise request.MissingWSGIRemoteUser()

        # We have a username.  Fetch the (or create a) matching user record.
        try:
            db.setUser(dbutils.User.fromName(db, username))
        except dbutils.NoSuchUser:
            email = getUserEmailAddress(username)
            db.setUser(
                dbutils.User.create(db,
                                    username,
                                    username,
                                    email,
                                    email_verified=None))
        return

    # Step 2: If cookie based sessions are used, check if there is a valid
    #         session cookie.
    if configuration.base.SESSION_TYPE == "cookie":
        sid = req.cookies.get("sid")
        if sid:
            cursor = db.cursor()
            cursor.execute(
                """SELECT uid, labels, EXTRACT('epoch' FROM NOW() - atime) AS age
                     FROM usersessions
                    WHERE key=%s""", (sid, ))

            row = cursor.fetchone()
            if row:
                user_id, labels, session_age = row

                if configuration.base.SESSION_MAX_AGE == 0 \
                        or session_age < configuration.base.SESSION_MAX_AGE:
                    # This is a valid session cookie.
                    user = dbutils.User.fromId(db, user_id)
                    if labels is None:
                        labels = auth.DATABASE.getAuthenticationLabels(user)
                    else:
                        labels = labels.split("|") if labels else ()
                    db.setUser(user, labels)
                    return

                # The session cookie is too old.  Delete it from the database.
                with db.updating_cursor("usersessions") as cursor:
                    cursor.execute(
                        """DELETE FROM usersessions
                                            WHERE key=%s""", (sid, ))

            # The session cookie is not valid.  Delete it from the browser.
            req.deleteCookie("sid")
            # Also delete the has_sid cookie, if there is one.
            req.deleteCookie("has_sid")

            # Since the session seems to have expired, offer the user to sign in
            # again by redirecting to the login page.  Signing in is optional
            # though, meaning the login page will have a "Continue anonymously"
            # link (if anonymous access is allowed.)
            #
            # Exception: Don't do this if /login is being requested.
            if req.allowRedirect(307) and req.path != "login":
                raise request.NeedLogin(req, optional=True)

        elif req.cookies.get("has_sid") == "1":
            # The request had no session cookie, but had the has_sid cookie that
            # indicates the browser ought to have a sesssion cookie.  Typically,
            # this means a signed in user accesses a mixed HTTP/HTTPS system
            # over HTTP.  If so, redirect the user to HTTPS.
            req.ensureSecure()

            # The above call would have raised if a redirect was meaningful.  If
            # it didn't, the has_sid cookie is bogus, so delete it.
            req.deleteCookie("has_sid")

        elif req.cookies.get("has_sid") == "0":
            # This indicates that the user just signed out.  If anonymous access
            # is not allowed, we'll redirect the user to the login page again,
            # which is sort of a bit unhelpful.
            #
            # Worse yet; if use of an external authentication provider is
            # enforced, the login page will redirect there, which might sign the
            # user back in, non-interactively.  In that case, signing out would
            # be impossible.
            #
            # So, instead, detect the sign-out and return a simple "you have
            # signed out" page in this case.

            # Delete the cookie.  This means that on reload, the user is
            # redirected to the login page again.  (This is to prevent the user
            # from getting stuck on this "you have signed out" page.)
            req.deleteCookie("has_sid")

            # Do the redirect if anonymous access isn't allowed.  Also don't do
            # it on the actual login page.
            if not configuration.base.ALLOW_ANONYMOUS_USER \
                    and req.path != "login":
                raise request.DisplayMessage(
                    title="You have signed out",
                    body="To use this system, you will need to sign in again.")

    # Step 3(a): Check if there's a valid HTTP Authorization header (even if
    #            cookie based sessions are typically used.)  If there is such a
    #            header, we assume HTTP authentication was meant to be used, and
    #            respond with a 401 Unauthorized response if authentication
    #            using the header fails.
    authorization_header = req.getRequestHeader("Authorization")
    if authorization_header:
        import base64

        try:
            authtype, base64_credentials = authorization_header.split(None, 1)
        except ValueError:
            authtype = "invalid"
        if authtype.lower() != "basic":
            raise request.RequestHTTPAuthentication()

        try:
            credentials = base64.b64decode(base64_credentials)
        except (ValueError, TypeError) as error:
            raise request.RequestHTTPAuthentication()

        username, _, password = credentials.partition(":")
        username = username.strip()

        if username and password:
            try:
                auth.DATABASE.performHTTPAuthentication(db, username, password)
                req.session_type = "httpauth"
                return
            except auth.AuthenticationFailed:
                pass

        raise request.RequestHTTPAuthentication()

    # Step 3(b): If the request has a "use_httpauth" cookie, request/require
    #            HTTP authentication.  This is a just a convenience feature for
    #            clients using HTTP stacks that only send credentials in
    #            response to server challenges.  (If cookie sessions are used,
    #            no such challenge would normally be returned, we'd rather
    #            redirect to the login page.)
    if req.cookies.get("use_httpauth"):
        raise request.RequestHTTPAuthentication()
    # Also do this for requests with a "httpauth=yes" query parameter.
    if req.getParameter("httpauth", "no") == "yes":
        raise request.RequestHTTPAuthentication()

    # Step 4: If anonymous access is supported or if it should be allowed as an
    #         exception for the accessed path, leave the session anonymous.
    if configuration.base.ALLOW_ANONYMOUS_USER or isInsecurePath(req):
        db.setUser(dbutils.User.makeAnonymous())
        req.session_type = None
        return

    # Step 5: If HTTP authentication is required (i.e. no session cookies) then
    #         request that now.
    if configuration.base.SESSION_TYPE == "httpauth":
        raise request.RequestHTTPAuthentication()

    # Step 6: Cookie based sessions are enabled, and not anonymous access.  If
    #         this is a POST or PUT request, respond with 403 Forbidden, and
    #         otherwise redirect to the login page.
    if not req.allowRedirect(307):
        raise request.Forbidden("Valid user session required")

    raise request.NeedLogin(req, optional=req.cookies.has_key("has_sid"))
Exemple #8
0
def main(environ, start_response):
    request_start = time.time()

    db = dbutils.Database()
    user = None

    try:
        try:
            req = request.Request(db, environ, start_response)

            if req.user is None:
                if configuration.base.AUTHENTICATION_MODE == "critic":
                    if configuration.base.SESSION_TYPE == "httpauth":
                        req.setStatus(401)
                        req.addResponseHeader("WWW-Authenticate", "Basic realm=\"Critic\"")
                        req.start()
                        return
                    elif configuration.base.ALLOW_ANONYMOUS_USER or req.path in ("login", "validatelogin"):
                        user = dbutils.User.makeAnonymous()
                    elif req.method == "GET":
                        raise page.utils.NeedLogin, req
                    else:
                        # Don't try to redirect POST requests to the login page.
                        req.setStatus(403)
                        req.start()
                        return
            else:
                try:
                    user = dbutils.User.fromName(db, req.user)
                except dbutils.NoSuchUser:
                    cursor = db.cursor()
                    cursor.execute("""INSERT INTO users (name, email, fullname)
                                           VALUES (%s, %s, %s)
                                        RETURNING id""",
                                   (req.user, getUserEmailAddress(req.user), req.user))
                    user = dbutils.User.fromId(db, cursor.fetchone()[0])
                    db.commit()

            user.loadPreferences(db)

            if user.status == 'retired':
                cursor = db.cursor()
                cursor.execute("UPDATE users SET status='current' WHERE id=%s", (user.id,))
                user = dbutils.User.fromId(db, user.id)
                db.commit()

            if not user.getPreference(db, "debug.profiling.databaseQueries"):
                db.disableProfiling()

            if not req.path:
                if user.isAnonymous():
                    location = "tutorial"
                else:
                    location = user.getPreference(db, "defaultPage")

                if req.query:
                    location += "?" + req.query

                req.setStatus(307)
                req.addResponseHeader("Location", location)
                req.start()
                return

            if req.path == "redirect":
                target = req.getParameter("target", "/")

                if req.method == "POST":
                    # Don't use HTTP redirect for POST requests.

                    req.setContentType("text/html")
                    req.start()

                    yield "<meta http-equiv='refresh' content='0; %s'>" % htmlify(target)
                    return
                else:
                    raise page.utils.MovedTemporarily, target

            if req.path.startswith("!/"):
                req.path = req.path[2:]
            elif configuration.extensions.ENABLED:
                handled = extensions.executePage(db, req, user)
                if handled:
                    req.start()
                    yield handled
                    return

            if req.path.startswith("r/"):
                req.query = "id=" + req.path[2:] + ("&" + req.query if req.query else "")
                req.path = "showreview"

            if configuration.extensions.ENABLED:
                match = RE_EXTENSION_RESOURCE.match(req.path)
                if match:
                    content_type, resource = extensions.getExtensionResource(req, db, user, match.group(1))
                    if resource:
                        req.setContentType(content_type)
                        req.start()
                        yield resource
                        return
                    else:
                        req.setStatus(404)
                        req.start()
                        return

            if req.path.startswith("download/"): operation = download
            else: operation = operations.get(req.path)
            if operation:
                req.setContentType("text/plain")

                try: result = operation(req, db, user)
                except OperationError, error: result = error
                except page.utils.DisplayMessage, message:
                    result = "error:" + message.title
                    if message.body: result += "  " + message.body
                except Exception, exception: result = "error:\n" + "".join(traceback.format_exception(*sys.exc_info()))

                if isinstance(result, (OperationResult, OperationError)):
                    req.setContentType("text/json")

                    if isinstance(result, OperationResult):
                        if db.profiling: result.set("__profiling__", formatDBProfiling(db))
                        result.addResponseHeaders(req)
                else:
                    req.setContentType("text/plain")

                req.start()

                if isinstance(result, unicode): yield result.encode("utf8")
                else: yield str(result)

                return
Exemple #9
0
def main(environ, start_response):
    request_start = time.time()

    db = dbutils.Database()
    user = None

    try:
        try:
            req = request.Request(db, environ, start_response)

            if configuration.base.AUTHENTICATION_MODE == "critic" and req.user is None:
                req.setStatus(401)
                req.addResponseHeader("WWW-Authenticate", "Basic realm=\"Critic\"")
                req.start()
                return

            try:
                user = dbutils.User.fromName(db, req.user)
            except dbutils.NoSuchUser:
                cursor.execute("""INSERT INTO users (name, email, fullname)
                                       VALUES (%s, %s, %s)
                                    RETURNING id""",
                               (req.user, getUserEmailAddress(req.user), req.user))
                user = dbutils.User.fromId(db, cursor.fetchone()[0])
                db.commit()

            user.loadPreferences(db)

            if user.status == 'retired':
                cursor = db.cursor()
                cursor.execute("UPDATE users SET status='current' WHERE id=%s", (user.id,))
                user = dbutils.User.fromId(db, user.id)
                db.commit()

            if not user.getPreference(db, "debug.profiling.databaseQueries"):
                db.disableProfiling()

            if not req.path:
                location = user.getPreference(db, "defaultPage")

                if req.query:
                    location += "?" + req.query

                req.setStatus(307)
                req.addResponseHeader("Location", location)
                req.start()
                return

            if req.path.startswith("!/"):
                req.path = req.path[2:]
            elif configuration.extensions.ENABLED:
                handled = extensions.executePage(db, req, user)
                if handled:
                    req.start()
                    yield handled
                    return

            if req.path.startswith("r/"):
                req.query = "id=" + req.path[2:] + ("&" + req.query if req.query else "")
                req.path = "showreview"

            if configuration.extensions.ENABLED:
                match = RE_EXTENSION_RESOURCE.match(req.path)
                if match:
                    content_type, resource = extensions.getExtensionResource(req, db, user, match.group(1))
                    if resource:
                        req.setContentType(content_type)
                        req.start()
                        yield resource
                        return
                    else:
                        req.setStatus(404)
                        req.start()
                        return

            if req.path.startswith("download/"): operation = download
            else: operation = operations.get(req.path)
            if operation:
                req.setContentType("text/plain")

                try: result = operation(req, db, user)
                except OperationError, error: result = error
                except page.utils.DisplayMessage, message:
                    result = "error:" + message.title
                    if message.body: result += "  " + message.body
                except Exception, exception: result = "error:\n" + "".join(traceback.format_exception(*sys.exc_info()))

                if isinstance(result, (OperationResult, OperationError)):
                    req.setContentType("text/json")

                    if db.profiling and isinstance(result, OperationResult):
                        result.set("__profiling__", formatDBProfiling(db))
                else:
                    req.setContentType("text/plain")

                req.start()

                if isinstance(result, unicode): yield result.encode("utf8")
                else: yield str(result)

                return