/usr/lib/python3/dist-packages/klaus/__init__.py is in python3-klaus 1.2.1-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | import jinja2
import flask
import dulwich.web
from klaus import views, utils
from klaus import httpauth
from klaus.repo import FancyRepo
KLAUS_VERSION = utils.guess_git_revision() or '1.2.1'
class Klaus(flask.Flask):
jinja_options = {
'extensions': ['jinja2.ext.autoescape'],
'undefined': jinja2.StrictUndefined
}
def __init__(self, repo_paths, site_name, use_smarthttp, ctags_policy='none'):
"""(See `make_app` for parameter descriptions.)"""
repo_objs = [FancyRepo(path) for path in repo_paths]
self.repos = dict((repo.name, repo) for repo in repo_objs)
self.site_name = site_name
self.use_smarthttp = use_smarthttp
self.ctags_policy = ctags_policy
flask.Flask.__init__(self, __name__)
self.setup_routes()
def create_jinja_environment(self):
"""Called by Flask.__init__"""
env = super(Klaus, self).create_jinja_environment()
for func in [
'force_unicode',
'timesince',
'shorten_sha1',
'shorten_message',
'extract_author_name',
'formattimestamp',
]:
env.filters[func] = getattr(utils, func)
env.globals['KLAUS_VERSION'] = KLAUS_VERSION
env.globals['USE_SMARTHTTP'] = self.use_smarthttp
env.globals['SITE_NAME'] = self.site_name
return env
def setup_routes(self):
for endpoint, rule in [
('repo_list', '/'),
('robots_txt', '/robots.txt/'),
('blob', '/<repo>/blob/'),
('blob', '/<repo>/blob/<rev>/<path:path>'),
('blame', '/<repo>/blame/'),
('blame', '/<repo>/blame/<rev>/<path:path>'),
('raw', '/<repo>/raw/<path:path>/'),
('raw', '/<repo>/raw/<rev>/<path:path>'),
('submodule', '/<repo>/submodule/<rev>/'),
('submodule', '/<repo>/submodule/<rev>/<path:path>'),
('commit', '/<repo>/commit/<path:rev>/'),
('patch', '/<repo>/commit/<path:rev>.diff'),
('patch', '/<repo>/commit/<path:rev>.patch'),
('index', '/<repo>/'),
('index', '/<repo>/<path:rev>'),
('history', '/<repo>/tree/<rev>/'),
('history', '/<repo>/tree/<rev>/<path:path>'),
('download', '/<repo>/tarball/<path:rev>/'),
]:
self.add_url_rule(rule, view_func=getattr(views, endpoint))
def should_use_ctags(self, git_repo, git_commit):
if self.ctags_policy == 'none':
return False
elif self.ctags_policy == 'ALL':
return True
elif self.ctags_policy == 'tags-and-branches':
return git_commit.id in git_repo.get_tag_and_branch_shas()
else:
raise ValueError("Unknown ctags policy %r" % self.ctags_policy)
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.ProxyFix(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.ProxyFix(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
|