-
Notifications
You must be signed in to change notification settings - Fork 1
/
views.py
132 lines (111 loc) · 4.44 KB
/
views.py
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
import requests
from django import http
from django.shortcuts import reverse
from django.conf import settings
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import permission_required
from notifications.utils import notify
from dkron import models, utils
def auth(request):
# great thing User object is already filled in because Cookies were shared anyway (assuming ProxyPass in nginx)
# cannot use decorator though because of redirect... fail response needs to be 401 or 403
# use 401 so nginx redirects to login
if not request.user.is_authenticated:
return http.HttpResponse(status=401)
# and 403 to display access forbidden
if not request.user.has_perm('dkron.can_use_dashboard'):
return http.HttpResponseForbidden()
return http.HttpResponse()
@csrf_exempt
def webhook(request):
if settings.DKRON_TOKEN is None:
return http.HttpResponseNotFound()
if request.method != 'POST':
return http.HttpResponseBadRequest()
lines = request.body.decode().splitlines()
if len(lines) != 3:
return http.HttpResponseBadRequest()
if lines[0] != settings.DKRON_TOKEN:
return http.HttpResponseForbidden()
o = models.Job.objects.filter(name=lines[1]).first()
if o is None:
return http.HttpResponseNotFound()
o.last_run_success = lines[2] == 'true'
o.last_run_date = timezone.now()
o.save()
if not o.last_run_success and o.notify_on_error:
notify(
'dkron_failed_job',
f''':red-pipeline: dkron job *{o.name}* <{request.build_absolute_uri(
utils.job_executions(o.name)
)}|failed>''',
)
return http.HttpResponse()
@permission_required('dkron.can_use_dashboard')
@csrf_exempt
def proxy(request, path=None):
"""
reverse proxy implementation based on https://github.com/mjumbewu/django-proxy/blob/master/proxy/views.py
this is a simplified implementation that "just works" for Dkron but is definitely missing cases for reverse proxying other backends
re-use in other projects at your own discretion :)
refer to `dkron authentication` section in docs #FIXME
"""
headers = {
k: v
for k, v in request.headers.items()
# content-length is not used by requests and might get duplicated (due to casing), just remove it
# also, dkron does not use cookies, simply drop the whole header to avoid sending django app session over
if k.lower() not in ('content-length', 'cookie')
}
url = settings.DKRON_URL + (path or '')
response = requests.request(
request.method,
url,
allow_redirects=False,
headers=headers,
params=request.GET.copy(),
data=request.body,
)
proxy_response = http.HttpResponse(response.content, status=response.status_code)
excluded_headers = set(
[
# Hop-by-hop headers
# ------------------
# Certain response headers should NOT be just tunneled through. These
# are they. For more info, see:
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
'connection',
'keep-alive',
'proxy-authenticate',
'proxy-authorization',
'te',
'trailers',
'transfer-encoding',
'upgrade',
# Although content-encoding is not listed among the hop-by-hop headers,
# it can cause trouble as well. Just let the server set the value as
# it should be.
'content-encoding',
# Since the remote server may or may not have sent the content in the
# same encoding as Django will, let Django worry about what the length
# should be.
'content-length',
]
)
for key, value in response.headers.items():
if key.lower() in excluded_headers:
continue
elif key.lower() == 'location':
proxy_response[key] = _fix_location_header(path, value)
else:
proxy_response[key] = value
return proxy_response
def _fix_location_header(path, location):
base = reverse('dkron:proxy')
if location.startswith(settings.DKRON_URL):
return base + location[len(settings.DKRON_URL) :]
elif location.startswith('/'):
return base + location[1:]
else:
return base + (path or '') + '/' + location