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