95 lines
3.4 KiB
Python
95 lines
3.4 KiB
Python
# passerelle - uniform access to multiple data sources and services
|
|
# Copyright (C) 2023 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
from urllib.parse import parse_qsl
|
|
|
|
from django.db import models
|
|
from django.http.response import HttpResponse
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from passerelle.base.models import BaseResource, HTTPResource
|
|
from passerelle.utils.api import endpoint
|
|
|
|
PASS_HEADERS_REQUEST = (
|
|
'accept',
|
|
'accept-encoding',
|
|
'accept-language',
|
|
'cookie',
|
|
'user-agent',
|
|
)
|
|
|
|
|
|
class Resource(BaseResource, HTTPResource):
|
|
category = _('Misc')
|
|
|
|
upstream_base_url = models.URLField(_('Upstream Service Base URL'))
|
|
http_timeout = models.PositiveIntegerField(_('Timeout on upstream (in seconds)'), default=20)
|
|
forced_headers = models.TextField(
|
|
_('Headers'),
|
|
blank=True,
|
|
help_text=_('Headers to always add (one per line, format "Header-Name: value")'),
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = _('Proxy')
|
|
|
|
@endpoint(
|
|
name='request',
|
|
perm='can_access',
|
|
methods=['get', 'post', 'delete', 'put', 'patch'],
|
|
pattern=r'^(?P<path>.*)$',
|
|
description=_('Make a request'),
|
|
example_pattern='{path}',
|
|
parameters={
|
|
'path': {
|
|
'description': _('request will be made on Upstream Service Base URL + path'),
|
|
'example_value': 'foo/bar',
|
|
}
|
|
},
|
|
)
|
|
def request(self, request, path, *args, **kwargs):
|
|
params = parse_qsl(request.META.get('QUERY_STRING'))
|
|
if params and params[-1][0] == 'signature':
|
|
# remove Publik signature parts: orig, algo, timestamp, nonce, signature
|
|
params = [
|
|
(k, v) for k, v in params if k not in ('orig', 'algo', 'timestamp', 'nonce', 'signature')
|
|
]
|
|
headers = {k: v for k, v in request.headers.items() if v and k.lower() in PASS_HEADERS_REQUEST}
|
|
if request.method != 'GET':
|
|
headers['Content-Type'] = request.headers.get('content-type')
|
|
for header in self.forced_headers.splitlines():
|
|
header = header.strip()
|
|
if header.startswith('#'):
|
|
continue
|
|
header = header.split(':', 1)
|
|
if len(header) == 2:
|
|
headers[header[0].strip()] = header[1].strip()
|
|
upstream = self.requests.request(
|
|
method=request.method,
|
|
url=self.upstream_base_url + path,
|
|
headers=headers,
|
|
params=params,
|
|
data=request.body,
|
|
timeout=self.http_timeout,
|
|
)
|
|
response = HttpResponse(
|
|
upstream.content,
|
|
content_type=upstream.headers.get('Content-Type'),
|
|
status=upstream.status_code,
|
|
reason=upstream.reason,
|
|
)
|
|
return response
|