Ejemplo n.º 1
0
def make_app(repos, site_name, git_bin, clone_urls, use_smarthttp=False, htdigest_file=None):
    """
    Returns a WSGI app with all the features (smarthttp, authentication)
    already patched in.
    """
    app = Klaus(
        repos,
        site_name,
        git_bin,
        clone_urls,
        use_smarthttp,
    )
    app.wsgi_app = utils.SubUri(app.wsgi_app)

    if use_smarthttp:
        # `path -> Repo` mapping for Dulwich's web support
        dulwich_backend = dulwich.server.DictBackend(
            dict(('/'+repo.name, repo) for repo in app.repos)
        )
        # Dulwich takes care of all Git related requests/URLs
        # and passes through everything else to klaus
        dulwich_wrapped_app = dulwich.web.make_wsgi_chain(
            backend=dulwich_backend,
            fallback_app=app.wsgi_app,
        )

        # `receive-pack` is requested by the "client" on a push
        # (the "server" is asked to *receive* packs), i.e. we need to secure
        # it using authentication or deny access completely to make the repo
        # read-only.
        #
        # Git first sends requests to /<repo-name>/info/refs?service=git-receive-pack.
        # If this request is responded to using HTTP 401 Unauthorized, the user
        # is prompted for username and password. If we keep responding 401, Git
        # interprets this as an authentication failure.  (We can't respond 403
        # because this results in horrible, unhelpful Git error messages.)
        #
        # Git will never call /<repo-name>/git-receive-pack if authentication
        # failed for /info/refs, but since it's used to upload stuff to the server
        # we must secure it anyway for security reasons.
        PATTERN = r'^/[^/]+/(info/refs\?service=git-receive-pack|git-receive-pack)$'
        if htdigest_file:
            # .htdigest file given. Use it to read the push-er credentials from.
            app.wsgi_app = httpauth.DigestFileHttpAuthMiddleware(
                htdigest_file,
                wsgi_app=utils.SubUri(dulwich_wrapped_app),
                routes=[PATTERN],
            )
        else:
            # no .htdigest file given. Disable push-ing.  Semantically we should
            # use HTTP 403 here but since that results in freaky error messages
            # (see above) we keep asking for authentication (401) instead.
            # Git will print a nice error message after a few tries.
            app.wsgi_app = httpauth.AlwaysFailingAuthMiddleware(
                wsgi_app=utils.SubUri(dulwich_wrapped_app),
                routes=[PATTERN],
            )

    return app
Ejemplo n.º 2
0
def make_app(repo_paths,
             site_name,
             use_smarthttp=False,
             htdigest_file=None,
             require_browser_auth=False,
             disable_push=False,
             unauthenticated_push=False,
             ctags_policy='none'):
    """
    Returns a WSGI app with all the features (smarthttp, authentication)
    already patched in.

    :param repo_paths: List of paths of repositories to serve.
    :param site_name: Name of the Web site (e.g. "John Doe's Git Repositories")
    :param use_smarthttp: Enable Git Smart HTTP mode, which makes it possible to
        pull from the served repositories. If `htdigest_file` is set as well,
        also allow to push for authenticated users.
    :param require_browser_auth: Require HTTP authentication according to the
        credentials in `htdigest_file` for ALL access to the Web interface.
        Requires the `htdigest_file` option to be set.
    :param disable_push: Disable push support. This is required in case both
        `use_smarthttp` and `require_browser_auth` (and thus `htdigest_file`)
        are set, but push should not be supported.
    :param htdigest_file: A *file-like* object that contains the HTTP auth credentials.
    :param unauthenticated_push: Allow push'ing without authentication. DANGER ZONE!
    :param ctags_policy: The ctags policy to use, may be one of:
        - 'none': never use ctags
        - 'tags-and-branches': use ctags for revisions that are the HEAD of
          a tag or branc
        - 'ALL': use ctags for all revisions, may result in high server load!
    """
    if unauthenticated_push:
        if not use_smarthttp:
            raise ValueError(
                "'unauthenticated_push' set without 'use_smarthttp'")
        if disable_push:
            raise ValueError("'unauthenticated_push' set with 'disable_push'")
        if require_browser_auth:
            raise ValueError(
                "Incompatible options 'unauthenticated_push' and 'require_browser_auth'"
            )
    if htdigest_file and not (require_browser_auth or use_smarthttp):
        raise ValueError(
            "'htdigest_file' set without 'use_smarthttp' or 'require_browser_auth'"
        )

    app = Klaus(
        repo_paths,
        site_name,
        use_smarthttp,
        ctags_policy,
    )
    app.wsgi_app = utils.SubUri(app.wsgi_app)

    if use_smarthttp:
        # `path -> Repo` mapping for Dulwich's web support
        dulwich_backend = dulwich.server.DictBackend(
            dict(('/' + name, repo) for name, repo in app.repos.items()))
        # Dulwich takes care of all Git related requests/URLs
        # and passes through everything else to klaus
        dulwich_wrapped_app = dulwich.web.make_wsgi_chain(
            backend=dulwich_backend,
            fallback_app=app.wsgi_app,
        )
        dulwich_wrapped_app = utils.SubUri(dulwich_wrapped_app)

        # `receive-pack` is requested by the "client" on a push
        # (the "server" is asked to *receive* packs), i.e. we need to secure
        # it using authentication or deny access completely to make the repo
        # read-only.
        #
        # Git first sends requests to /<repo-name>/info/refs?service=git-receive-pack.
        # If this request is responded to using HTTP 401 Unauthorized, the user
        # is prompted for username and password. If we keep responding 401, Git
        # interprets this as an authentication failure.  (We can't respond 403
        # because this results in horrible, unhelpful Git error messages.)
        #
        # Git will never call /<repo-name>/git-receive-pack if authentication
        # failed for /info/refs, but since it's used to upload stuff to the server
        # we must secure it anyway for security reasons.
        PATTERN = r'^/[^/]+/(info/refs\?service=git-receive-pack|git-receive-pack)$'
        if unauthenticated_push:
            # DANGER ZONE: Don't require authentication for push'ing
            app.wsgi_app = dulwich_wrapped_app
        elif htdigest_file and not disable_push:
            # .htdigest file given. Use it to read the push-er credentials from.
            if require_browser_auth:
                # No need to secure push'ing if we already require HTTP auth
                # for all of the Web interface.
                app.wsgi_app = dulwich_wrapped_app
            else:
                # Web interface isn't already secured. Require authentication for push'ing.
                app.wsgi_app = httpauth.DigestFileHttpAuthMiddleware(
                    htdigest_file,
                    wsgi_app=dulwich_wrapped_app,
                    routes=[PATTERN],
                )
        else:
            # No .htdigest file given. Disable push-ing.  Semantically we should
            # use HTTP 403 here but since that results in freaky error messages
            # (see above) we keep asking for authentication (401) instead.
            # Git will print a nice error message after a few tries.
            app.wsgi_app = httpauth.AlwaysFailingAuthMiddleware(
                wsgi_app=dulwich_wrapped_app,
                routes=[PATTERN],
            )

    if require_browser_auth:
        app.wsgi_app = httpauth.DigestFileHttpAuthMiddleware(
            htdigest_file, wsgi_app=app.wsgi_app)

    return app