import base64
import http.client
import db.holder
from model.model import Model
from answer import Result
from translator import Translator
class RepoProxy:
SKIP_HEADERS = {
'connection',
'keep-alive',
'proxy-authenticate',
'proxy-authorization',
'proxy-connection',
'te',
'trailers',
'transfer-encoding',
'upgrade' }
def __init__(self, server):
self.server = server
def unauthorized(self):
return Result(
'401 Unauthorized',
[('WWW-Authenticate', 'Basic ream="Authorization area", charset="UTF-8"')],
list('401 Unauthorized') )
def forbidden(self):
return Result('403 Forbidden', list(), list('403 Forbidden'))
def badgateway(self):
return Result('502 Bad Gateway', list(), list('502 Bad Gateway'))
def split(self, text, separator, left = False):
x = text.split(separator, 1)
if len(x) < 2:
return ('', x[0]) if left else (x[0], '')
return x
def input_reader(self, request):
data = request.env['wsgi.input']
while True:
chunk = data.read(4096)
if not chunk: break
yield chunk
def reader(self, connection, response):
while True:
chunk = response.read(4096)
if not chunk: break
yield chunk
connection.close()
def proxy(self, request, fullurl):
s = fullurl
protocol, s = self.split(s, '://')
host, s = self.split(s, '/')
url = '/' + s
auth, host = self.split(host, '@', True)
host, port = self.split(host, ':')
if not port: port = None
if not host or not url:
print('repoproxy: bad url [' + host + '][' + url + '][' + fullurl + ']')
return self.badgateway()
if auth:
print('repoproxy: authorization in url is not supported [' + auth + '][' + fullurl + ']')
return self.badgateway()
connection_class = None
if protocol == 'https':
connection_class = http.client.HTTPSConnection
elif protocol == 'http':
connection_class = http.client.HTTPConnection
if not connection_class:
print('repoproxy: protocol is not supported [' + protocol + '][' + fullurl + ']')
return self.badgateway()
body = self.input_reader(request)
header_prefix = 'HTTP_'
proxy_headers = dict()
for k, v in request.env.items():
if k.startswith(header_prefix):
kk = k[len(header_prefix):].lower().replace('_', '-')
if not kk in self.SKIP_HEADERS:
proxy_headers[kk] = v
if 'CONTENT_TYPE' in request.env:
proxy_headers['content-type'] = request.env['CONTENT_TYPE']
proxy_headers['host'] = host
connection = None
try:
connection = connection_class(host = host, port = port)
connection.request(
request.method,
url,
body = body,
headers = proxy_headers )
except Exception as e:
print('repoproxy: connection failed [' + fullurl + ']')
print(e)
return self.badgateway()
response = connection.getresponse()
headers = list()
for k, v in response.getheaders():
if not k.lower() in self.SKIP_HEADERS:
headers.append( (k, v) )
return Result(str(response.status) + ' ' + str(response.reason), headers, self.reader(connection, response))
def process(self, request, path):
if len(path) < 2:
return self.forbidden()
repoowner = path[0]
reponame = path[1]
nextpath = path[2:]
if request.method != 'GET' and request.method != 'POST':
return self.forbidden()
post = request.method == 'POST'
login = ''
password = ''
if 'HTTP_AUTHORIZATION' in request.env:
try:
authtype, credentials = str(request.env['HTTP_AUTHORIZATION']).split()
assert authtype.lower() == 'basic'
login, password = base64.b64decode(credentials).decode('utf8').split(':')
except Exception:
return self.unauthorized()
url = None
with db.holder.Holder(request.server.dbpool, readonly = True) as connection:
request.connection = connection
request.model = Model(connection, Translator(), superuser = True)
user = None
if login:
user_id = request.model.users.check_password(login, password)
if not user_id:
return self.unauthorized()
user = request.model.users.get_by_id(user_id)
assert(user)
elif post:
return self.unauthorized()
owner = request.model.users.get_by_login( repoowner )
if not owner:
return self.forbidden()
repo = request.model.repositories.get_by_name( owner.id, reponame, owner )
if not repo:
return self.forbidden()
if post:
if user.id != repo.user_id and not request.model.rights.get_superuser(user.id):
return self.unauthorized()
url = repo.gen_internalurl()
getvars = request.env.get('QUERY_STRING', '')
if nextpath:
url += '/' + '/'.join(nextpath)
if getvars:
url += '?' + getvars
return self.proxy(request, url)