Esempio n. 1
0
def findreview(req, db):
    sha1 = req.getParameter("sha1")

    try:
        repository = gitutils.Repository.fromSHA1(db, sha1)
        commit = gitutils.Commit.fromSHA1(db, repository, repository.revparse(sha1))
    except gitutils.GitReferenceError as error:
        raise request.DisplayMessage(error.message)

    cursor = db.readonly_cursor()
    cursor.execute("""SELECT reviews.id
                        FROM reviews
                        JOIN branches ON (branches.id=reviews.branch)
                        JOIN reachable ON (reachable.branch=branches.id)
                       WHERE reachable.commit=%s""",
                   (commit.getId(db),))

    row = cursor.fetchone()

    if row:
        review_id = row[0]
    else:
        cursor.execute("""SELECT reviewchangesets.review
                            FROM reviewchangesets
                            JOIN changesets ON (changesets.id=reviewchangesets.changeset)
                           WHERE changesets.child=%s""",
                       (commit.getId(db),))

        row = cursor.fetchone()

        if row:
            review_id = row[0]
        else:
            raise request.DisplayMessage("No review found!")

    raise request.MovedTemporarily("/r/%d?highlight=%s#%s" % (review_id, sha1, sha1))
Esempio n. 2
0
def handleMissingWSGIRemoteUser(db, req):
    return handleDisplayMessage(
        db, req, request.DisplayMessage(
            title="Configuration error",
            body="""\
<p>
Critic was configured with "<code>--auth-mode host</code>", meaning the host web
server will authenticate users, but there was no <code>REMOTE_USER</code>
variable in the WSGI environment provided by the web server, indicating it is
not actually configured to authenticate users.
</p>

<p>
To fix this you can either reinstall Critic using "<code>--auth-mode
critic</code>" (to let Critic handle user authentication internally using its
own user database), or you can configure user authentication properly in the web
server.  For Apache 2.x, the latter can be done by adding the something like the
following to the apache site configuration for Critic:
</p>

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

<p>
If you need more dynamic http authentication you can instead setup mod_wsgi with
a custom <code>WSGIAuthUserScript</code> 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.
</p>

<p>
For more information on setting up such authentication in Apache 2.x, see:
<a href="%(url)s">Apache Authentication Provider</a>.
</p>"""
            % { "url": ("http://code.google.com/p/modwsgi/wiki/"
                        "AccessControlMechanisms#Apache_Authentication_Provider") },
            html=True,
            status=500))
Esempio n. 3
0
def process_request(environ, start_response):
    request_start = time.time()

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

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

            # Handle static resources very early.  We don't bother with checking
            # for an authenticated user; static resources aren't sensitive, and
            # are referenced from special-case resources like the login page and
            # error messages that, that we want to display even if something
            # went wrong with the authentication.
            if req.path.startswith("static-resource/"):
                return handleStaticResource(req)

            if req.path.startswith("externalauth/"):
                provider_name = req.path[len("externalauth/"):]
                if provider_name in auth.PROVIDERS:
                    provider = auth.PROVIDERS[provider_name]
                    authorize_url = provider.start(db, req)
                    if authorize_url:
                        raise request.Found(authorize_url)

            if 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):
                        finishOAuth(db, req, provider)

            auth.checkSession(db, req)
            auth.AccessControl.accessHTTP(db, req)

            user = req.user
            user.loadPreferences(db)

            if user.status == 'retired':
                # If a retired user accesses the system, change the status back
                # to 'current' again.
                with db.updating_cursor("users") as cursor:
                    cursor.execute("""UPDATE users
                                         SET status='current'
                                       WHERE id=%s""",
                                   (user.id,))
                user.status = 'current'

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

            original_path = req.path

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

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

                raise request.MovedTemporarily(location)

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

            if req.path == "findreview":
                # This raises either DisplayMessage or MovedTemporarily.
                findreview(req, db)

            # 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 []

            # Extension "page" roles.  Prefixing a path with "!/" bypasses all
            # extensions.
            #
            # Also bypass extensions if the user is anonymous unless general
            # anonymous access is allowed.  If it's not and the user is still
            # anonymous, access was allowed because of a path-based exception,
            # which was not intended to allow access to an extension.
            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("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/"):
                return handleDownload(db, req, user)

            if req.path == "api" or req.path.startswith("api/"):
                try:
                    result = jsonapi.handleRequest(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)]

            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)]

            impersonate_user = user

            if not user.isAnonymous():
                user_parameter = req.getParameter("user", None)
                if user_parameter:
                    impersonate_user = dbutils.User.fromName(db, user_parameter)

            while True:
                pagefn = PAGES.get(req.path)
                if pagefn:
                    try:
                        result = pagefn(req, db, impersonate_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, page.utils.ResponseBody):
                            req.setContentType(result.content_type)
                            req.start()
                            return [result.data]

                        if isinstance(result, str) or isinstance(result, Document):
                            req.setContentType("text/html")
                            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]

                        result = WrappedResult(db, req, user, result)

                        req.setContentType("text/html")
                        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)

                if "/" in req.path:
                    repository_name, _, rest = req.path.partition("/")
                    repository = gitutils.Repository.fromName(db, repository_name)
                    if repository:
                        req.path = rest
                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 = user.getDefaultRepository(db)

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

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

                if repository:
                    try:
                        items = filter(None, map(revparse, req.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" % original_path,
                status=404)
        except GeneratorExit:
            raise
        except auth.AccessDenied as error:
            return handleDisplayMessage(
                db, req, request.DisplayMessage(
                    title="Access denied",
                    body=error.message,
                    status=403))
        except request.HTTPResponse as response:
            return response.execute(db, req)
        except request.MissingWSGIRemoteUser as error:
            return handleMissingWSGIRemoteUser(db, req)
        except page.utils.DisplayMessage as message:
            return handleDisplayMessage(db, req, message)
        except page.utils.DisplayFormattedText as formatted_text:
            return handleDisplayFormattedText(db, req, formatted_text)
        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()
Esempio n. 4
0
        def generateContent(self):
            trackedbranch = self.review.getTrackedBranch(self.db)

            if not trackedbranch:
                raise request.DisplayMessage("Not supported!", "The review r/%d is not tracking a remote branch." % self.review.id)

            self.document.addInternalScript(self.review.repository.getJS())
            self.document.addInternalScript(self.review.getJS())
            self.document.addInternalScript("var trackedbranch = { remote: %s, name: %s };"
                                            % (htmlutils.jsify(trackedbranch.remote),
                                               htmlutils.jsify(trackedbranch.name)))

            table = page.utils.PaleYellowTable(self.body, "Rebase tracking review")

            def renderRemote(target):
                target.span("value inset", id="remote").text(trackedbranch.remote)
            def renderCurrentBranch(target):
                target.span("value inset", id="currentbranch").text("refs/heads/" + trackedbranch.name)

            table.addItem("Remote", renderRemote)
            table.addItem("Current branch", renderCurrentBranch)
            table.addSeparator()

            if self.newbranch is not None and self.upstream is not None and self.newhead is not None and self.newupstream is not None:
                import log.html
                import log.commitset

                sha1s = self.review.repository.revlist(included=[self.newhead], excluded=[self.newupstream])
                new_commits = log.commitset.CommitSet(gitutils.Commit.fromSHA1(self.db, self.review.repository, sha1) for sha1 in sha1s)

                new_heads = new_commits.getHeads()
                if len(new_heads) != 1:
                    raise page.utils.DisplayMessage("Invalid commit-set!", "Multiple heads.  (This ought to be impossible...)")
                new_upstreams = new_commits.getFilteredTails(self.review.repository)
                if len(new_upstreams) != 1:
                    raise page.utils.DisplayMessage("Invalid commit-set!", "Multiple upstreams.  (This ought to be impossible...)")

                new_head = new_heads.pop()
                new_upstream_sha1 = new_upstreams.pop()

                old_commits = log.commitset.CommitSet(self.review.branch.commits)
                old_upstreams = old_commits.getFilteredTails(self.review.repository)

                if len(old_upstreams) != 1:
                    raise page.utils.DisplayMessage("Rebase not supported!", "The review has mulitple upstreams and can't be rebased.")

                if len(old_upstreams) == 1 and new_upstream_sha1 in old_upstreams:
                    # This appears to be a history rewrite.
                    new_upstream = None
                    new_upstream_sha1 = None
                    rebase_type = "history"
                else:
                    old_upstream = gitutils.Commit.fromSHA1(self.db, self.review.repository, old_upstreams.pop())
                    new_upstream = gitutils.Commit.fromSHA1(self.db, self.review.repository, new_upstream_sha1)

                    if old_upstream.isAncestorOf(new_upstream):
                        rebase_type = "move:ff"
                    else:
                        rebase_type = "move"

                self.document.addInternalScript("var check = { rebase_type: %s, old_head_sha1: %s, new_head_sha1: %s, new_upstream_sha1: %s, new_trackedbranch: %s };"
                                                % (htmlutils.jsify(rebase_type),
                                                   htmlutils.jsify(self.review.branch.head.sha1),
                                                   htmlutils.jsify(new_head.sha1),
                                                   htmlutils.jsify(new_upstream_sha1),
                                                   htmlutils.jsify(self.newbranch[len("refs/heads/"):])))

                def renderNewBranch(target):
                    target.span("value inset", id="newbranch").text(self.newbranch)
                    target.text(" @ ")
                    target.span("value inset").text(new_head.sha1[:8] + " " + new_head.niceSummary())
                def renderUpstream(target):
                    target.span("value inset", id="upstream").text(self.upstream)
                    target.text(" @ ")
                    target.span("value inset").text(new_upstream.sha1[:8] + " " + new_upstream.niceSummary())

                table.addItem("New branch", renderNewBranch)

                if new_upstream:
                    table.addItem("New upstream", renderUpstream)

                table.addSeparator()

                def renderMergeStatus(target):
                    target.a("status", id="status_merge").text("N/A")
                def renderConflictsStatus(target):
                    target.a("status", id="status_conflicts").text("N/A")
                def renderHistoryRewriteStatus(target):
                    target.a("status", id="status_historyrewrite").text("N/A")

                table.addSection("Status")

                if rebase_type == "history":
                    table.addItem("History rewrite", renderHistoryRewriteStatus)
                else:
                    if rebase_type == "move:ff":
                        table.addItem("Merge", renderMergeStatus)
                    table.addItem("Conflicts", renderConflictsStatus)

                def renderRebaseReview(target):
                    target.button(id="rebasereview", onclick="rebaseReview();", disabled="disabled").text("Rebase Review")

                table.addSeparator()
                table.addCentered(renderRebaseReview)

                log.html.render(self.db, self.body, "Rebased commits", commits=list(new_commits))
            else:
                try:
                    from customization.branches import getRebasedBranchPattern
                except ImportError:
                    def getRebasedBranchPattern(branch_name): return None

                pattern = getRebasedBranchPattern(trackedbranch.name)

                try:
                    from customization.branches import isRebasedBranchCandidate
                except ImportError:
                    isRebasedBranchCandidate = None

                if pattern or isRebasedBranchCandidate:
                    candidates = [name[len("refs/heads/"):]
                                  for sha1, name in gitutils.Repository.lsremote(trackedbranch.remote, pattern=pattern, include_heads=True)
                                  if name.startswith("refs/heads/")]

                    if isRebasedBranchCandidate is not None:
                        def isCandidate(name):
                            return isRebasedBranchCandidate(trackedbranch.name, name)

                        candidates = filter(isCandidate, candidates)
                else:
                    candidates = []

                if len(candidates) > 1:
                    def renderCandidates(target):
                        target.text("refs/heads/")
                        dropdown = target.select(id="newbranch")
                        for name in candidates:
                            dropdown.option(value=name).text(name)

                    table.addItem("New branch", renderCandidates,
                                    buttons=[("Edit", "editNewBranch(this);")])
                else:
                    if len(candidates) == 1:
                        default_value = candidates[0]
                    else:
                        default_value = trackedbranch.name

                    def renderEdit(target):
                        target.text("refs/heads/")
                        target.input(id="newbranch", value=default_value)

                    table.addItem("New branch", renderEdit)

                def renderUpstreamInput(target):
                    target.input(id="upstream", value="refs/heads/master")

                table.addItem("Upstream", renderUpstreamInput)

                def renderFetchBranch(target):
                    target.button(onclick="fetchBranch();").text("Fetch Branch")

                table.addSeparator()
                table.addCentered(renderFetchBranch)
Esempio n. 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"))