SHA256 hashes support for SSH keys.
[cas.git] / src / ssh_keys / views.py
1 from datetime import datetime, timedelta
2 import logging
3 import re
4 from django.contrib.auth.mixins import LoginRequiredMixin
5 from django.contrib import messages
6 from django.db import IntegrityError
7 from django.db.models import Q
8 from django import http
9 from django.shortcuts import get_object_or_404
10 from django.utils.timezone import now
11 from django.utils.translation import ugettext as _
12 from django.views.decorators.csrf import csrf_exempt
13 from django.views.generic import ListView, CreateView, DeleteView
14 from services.models import Service
15 from .models import SSHKey
16 from .utils import parse_log_line
17
18
19 class SSHKeysView(LoginRequiredMixin, ListView):
20     def get_queryset(self):
21         return SSHKey.objects.filter(user=self.request.user)
22
23
24 class AddSSHKeyView(LoginRequiredMixin, CreateView):
25     fields = ['key']
26     model = SSHKey
27     success_url = '/ssh/'
28     template_name = 'ssh_keys/sshkey_add.html'
29
30     def form_valid(self, form):
31         form.instance.user = self.request.user
32         try:
33             return super().form_valid(form)
34         except ValueError as e:
35             messages.add_message(self.request, messages.ERROR, e)
36         except IntegrityError:
37             messages.add_message(self.request, messages.ERROR, _("Key already in the database."))
38         return http.HttpResponseRedirect(self.success_url)
39
40
41     
42 class DeleteSSHKeyView(LoginRequiredMixin, DeleteView):
43     success_url = '/ssh/'
44
45     def get_queryset(self):
46         return SSHKey.objects.filter(user=self.request.user)
47
48
49 @csrf_exempt
50 def ssh_keys_seen(request):
51     logger = logging.getLogger('django.request')
52     key = request.GET.get('key')
53     service = get_object_or_404(Service, key=key)
54     n = now()
55
56     logline_re = r'^(?P<time>\w{3}\s+\d+\s+\d\d:\d\d:\d\d).*: Accepted publickey for .* from .* port .* ssh2: (?P<algo>\w+) (?P<hash>[a-f0-9:]+)$'
57
58     last_seen = {}
59     for line in request.body.decode('latin1').split('\n'):
60         if not line.strip():
61             continue
62         data = parse_log_line(line)
63         if data is None:
64             logger.error('Unparsed: ' + line)
65             break
66         dt = data['datetime']
67         algo = data['algo']
68         if 'md5' in data:
69             hash_type = 'md5'
70             hash_value = data['md5']
71         else:
72             hash_type = 'sha256'
73             hash_value = data['sha256']
74         key = algo, hash_type, hash_value
75         last_seen[key] = max(last_seen.get(key, dt), dt)
76
77     for key, dt in last_seen.items():
78         algo, hash_type, hash_value = key
79         SSHKey.objects.filter(
80             Q(last_seen_at=None) | Q(last_seen_at__lt=dt),
81             algorithm=algo,
82             **{f'{hash_type}_hash': hash_value}
83         ).update(
84             last_seen_at=dt
85         )
86
87     return http.HttpResponse('ok')
88