##// END OF EJS Templates
Added GET request handler, command for key generation. Changed KeyPair format for ecdsa (use to_string instead of to_pem because it's shorter)
neko259 -
r836:9ee107b9 decentral
parent child Browse files
Show More
@@ -0,0 +1,17 b''
1 __author__ = 'neko259'
2
3
4 from django.core.management import BaseCommand
5 from django.db import transaction
6
7 from boards.models import KeyPair
8
9
10 class Command(BaseCommand):
11 help = 'Generates the new keypair. The first one will be primary.'
12
13 @transaction.atomic
14 def handle(self, *args, **options):
15 key = KeyPair.objects.generate_key(
16 primary=not KeyPair.objects.has_primary())
17 print(key) No newline at end of file
@@ -0,0 +1,55 b''
1 from boards.models import KeyPair, Post
2 from boards.tests.mocks import MockRequest
3 from boards.views.sync import respond_get
4
5 __author__ = 'neko259'
6
7
8 from django.test import TestCase
9
10
11 class SyncTest(TestCase):
12 def test_get(self):
13 """
14 Forms a GET request of a post and checks the response.
15 """
16
17 key = KeyPair(public_key='pubkey', private_key='privkey',
18 key_type='test_key_type', primary=True)
19 key.save()
20
21 post = Post.objects.create_post(title='test_title', text='test_text')
22
23 request = MockRequest()
24 request.POST['xml'] = (
25 '<request type="get" version="1.0">'
26 '<model name="post" version="1.0">'
27 '<id key="%s" local-id="%d" type="%s" />'
28 '</model>'
29 '</request>' % (post.global_id.key,
30 post.id,
31 post.global_id.key_type)
32 )
33
34 self.assertTrue(
35 '<response>'
36 '<status>success</status>'
37 '<models>'
38 '<model name="post" ref-id="1">'
39 '<id key="%s" local-id="%d" type="%s" />'
40 '<title>%s</title>'
41 '<text>%s</text>'
42 '<pub-time>%d</pub-time>'
43 '<edit-time>%d</edit-time>'
44 '</model>'
45 '</models>'
46 '</response>' % (
47 post.global_id.key,
48 post.id,
49 post.global_id.key_type,
50 post.title,
51 post.text.raw,
52 post.get_pub_time_epoch(),
53 post.get_edit_time_epoch(),
54 ) in respond_get(request).content.decode(),
55 'Wrong response generated for the GET request.') No newline at end of file
@@ -24,9 +24,9 b' class GlobalId(models.Model):'
24 local_id = models.IntegerField()
24 local_id = models.IntegerField()
25
25
26 def __str__(self):
26 def __str__(self):
27 return '%s / %s / %d' % (self.key_type, self.key, self.local_id)
27 return '%s | %s | %d' % (self.key_type, self.key, self.local_id)
28
28
29 def to_xml_element(self, element: et.SubElement):
29 def to_xml_element(self, element: et.Element):
30 """
30 """
31 Exports global id to an XML element.
31 Exports global id to an XML element.
32 """
32 """
@@ -35,6 +35,28 b' class GlobalId(models.Model):'
35 element.set(ATTR_KEY_TYPE, self.key_type)
35 element.set(ATTR_KEY_TYPE, self.key_type)
36 element.set(ATTR_LOCAL_ID, str(self.local_id))
36 element.set(ATTR_LOCAL_ID, str(self.local_id))
37
37
38 @staticmethod
39 def from_xml_element(element: et.Element, existing=False):
40 """
41 Parses XML id tag and gets global id from it.
42
43 Arguments:
44 element -- the XML 'id' element
45 existing -- if this is False, a new instance of GlobalId will be
46 created. Otherwise, we will search for an existing GlobalId instance
47 and throw DoesNotExist if there isn't one.
48 """
49
50 if existing:
51 return GlobalId.objects.get(key=element.get(ATTR_KEY),
52 key_type=element.get(ATTR_KEY_TYPE),
53 local_id=int(element.get(
54 ATTR_LOCAL_ID)))
55 else:
56 return GlobalId(key=element.get(ATTR_KEY),
57 key_type=element.get(ATTR_KEY_TYPE),
58 local_id=int(element.get(ATTR_LOCAL_ID)))
59
38
60
39 class Signature(models.Model):
61 class Signature(models.Model):
40 class Meta:
62 class Meta:
@@ -16,8 +16,8 b' class KeyPairManager(models.Manager):'
16 private = SigningKey.generate()
16 private = SigningKey.generate()
17 public = private.get_verifying_key()
17 public = private.get_verifying_key()
18
18
19 private_key_str = private.to_pem().decode()
19 private_key_str = base64.b64encode(private.to_string()).decode()
20 public_key_str = public.to_pem().decode()
20 public_key_str = base64.b64encode(public.to_string()).decode()
21
21
22 return self.create(public_key=public_key_str,
22 return self.create(public_key=public_key_str,
23 private_key=private_key_str,
23 private_key=private_key_str,
@@ -27,7 +27,7 b' class KeyPairManager(models.Manager):'
27
27
28 def verify(self, public_key_str, string, signature, key_type=TYPE_ECDSA):
28 def verify(self, public_key_str, string, signature, key_type=TYPE_ECDSA):
29 if key_type == TYPE_ECDSA:
29 if key_type == TYPE_ECDSA:
30 public = VerifyingKey.from_pem(public_key_str)
30 public = VerifyingKey.from_string(base64.b64decode(public_key_str))
31 signature_byte = base64.b64decode(signature)
31 signature_byte = base64.b64decode(signature)
32 try:
32 try:
33 return public.verify(signature_byte, string.encode())
33 return public.verify(signature_byte, string.encode())
@@ -36,6 +36,9 b' class KeyPairManager(models.Manager):'
36 else:
36 else:
37 raise Exception('Key type not supported')
37 raise Exception('Key type not supported')
38
38
39 def has_primary(self):
40 return self.filter(primary=True).exists()
41
39
42
40 class KeyPair(models.Model):
43 class KeyPair(models.Model):
41 class Meta:
44 class Meta:
@@ -49,9 +52,10 b' class KeyPair(models.Model):'
49 primary = models.BooleanField(default=False)
52 primary = models.BooleanField(default=False)
50
53
51 def __str__(self):
54 def __str__(self):
52 return '%s: %s' % (self.key_type, self.public_key)
55 return '%s | %s' % (self.key_type, self.public_key)
53
56
54 def sign(self, string):
57 def sign(self, string):
55 private = SigningKey.from_pem(self.private_key)
58 private = SigningKey.from_string(base64.b64decode(
59 self.private_key.encode()))
56 signature_byte = private.sign(string.encode())
60 signature_byte = private.sign(string.encode())
57 return base64.b64encode(signature_byte)
61 return base64.b64encode(signature_byte)
@@ -35,7 +35,7 b''
35 {% endif %}
35 {% endif %}
36
36
37 {% if post.global_id %}
37 {% if post.global_id %}
38 <span class="global-id">{{ post.global_id.key_type }} / {{ post.global_id.key }} / {{ post.global_id.local_id }}</span>
38 <span class="global-id"> {{ post.global_id }} </span>
39 {% endif %}
39 {% endif %}
40
40
41 {% if moderator %}
41 {% if moderator %}
@@ -1,6 +1,33 b''
1 import xml.etree.ElementTree as et
2 from django.http import HttpResponse
3 from boards.models import GlobalId, Post
4
5
1 def respond_pull(request):
6 def respond_pull(request):
2 pass
7 pass
3
8
4
9
5 def respond_get(request):
10 def respond_get(request):
11 """
12 Processes a GET request with post ID list and returns the posts XML list.
13 Request should contain an 'xml' post attribute with the actual request XML.
14 """
15
16 request_xml = request.POST['xml']
17
18 posts = []
19
20 root_tag = et.fromstring(request_xml)
21 model_tag = root_tag[0]
22 for id_tag in model_tag:
23 try:
24 global_id = GlobalId.from_xml_element(id_tag, existing=True)
25 posts += Post.objects.filter(global_id=global_id)
26 except GlobalId.DoesNotExist:
27 # This is normal. If we don't have such GlobalId in the system,
28 # just ignore this ID and proceed to the next one.
6 pass
29 pass
30
31 response_xml = Post.objects.generate_response_get(posts)
32
33 return HttpResponse(content=response_xml) No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now