##// END OF EJS Templates
Added signatures to the GET response. Added a view to get a full post response for one post. Don't show post key as it is present in the XML post view. Changed key display format
neko259 -
r837:fbeaaa16 decentral
parent child Browse files
Show More
@@ -12,7 +12,7 b' from django.utils import timezone'
12
12
13 from markupfield.fields import MarkupField
13 from markupfield.fields import MarkupField
14
14
15 from boards.models import PostImage, KeyPair, GlobalId
15 from boards.models import PostImage, KeyPair, GlobalId, Signature
16 from boards.models.base import Viewable
16 from boards.models.base import Viewable
17 from boards.models.thread import Thread
17 from boards.models.thread import Thread
18 from boards import utils
18 from boards import utils
@@ -54,6 +54,8 b" TAG_PUB_TIME = 'pub-time'"
54 TAG_EDIT_TIME = 'edit-time'
54 TAG_EDIT_TIME = 'edit-time'
55 TAG_PREVIOUS = 'previous'
55 TAG_PREVIOUS = 'previous'
56 TAG_NEXT = 'next'
56 TAG_NEXT = 'next'
57 TAG_SIGNATURES = 'signatures'
58 TAG_SIGNATURE = 'signature'
57
59
58 TYPE_GET = 'get'
60 TYPE_GET = 'get'
59
61
@@ -61,6 +63,8 b" ATTR_VERSION = 'version'"
61 ATTR_TYPE = 'type'
63 ATTR_TYPE = 'type'
62 ATTR_NAME = 'name'
64 ATTR_NAME = 'name'
63 ATTR_REF_ID = 'ref-id'
65 ATTR_REF_ID = 'ref-id'
66 ATTR_MODEL_REF = 'model-ref'
67 ATTR_VALUE = 'value'
64
68
65 STATUS_SUCCESS = 'success'
69 STATUS_SUCCESS = 'success'
66
70
@@ -190,7 +194,6 b' class PostManager(models.Manager):'
190 cache.set(cache_key, ppd)
194 cache.set(cache_key, ppd)
191 return ppd
195 return ppd
192
196
193
194 def generate_request_get(self, model_list: list):
197 def generate_request_get(self, model_list: list):
195 """
198 """
196 Form a get request from a list of ModelId objects.
199 Form a get request from a list of ModelId objects.
@@ -217,13 +220,13 b' class PostManager(models.Manager):'
217 status.text = STATUS_SUCCESS
220 status.text = STATUS_SUCCESS
218
221
219 models = et.SubElement(response, TAG_MODELS)
222 models = et.SubElement(response, TAG_MODELS)
223 signatures = {}
220
224
221 ref_id = 1
225 ref_id = 1
222 for post in model_list:
226 for post in model_list:
223 model = et.SubElement(models, TAG_MODEL)
227 model = et.SubElement(models, TAG_MODEL)
224 model.set(ATTR_NAME, 'post')
228 model.set(ATTR_NAME, 'post')
225 model.set(ATTR_REF_ID, str(ref_id))
229 model.set(ATTR_REF_ID, str(ref_id))
226 ref_id += 1
227
230
228 tag_id = et.SubElement(model, TAG_ID)
231 tag_id = et.SubElement(model, TAG_ID)
229 post.global_id.to_xml_element(tag_id)
232 post.global_id.to_xml_element(tag_id)
@@ -252,7 +255,6 b' class PostManager(models.Manager):'
252 replied_post = Post.objects.get(id=id)
255 replied_post = Post.objects.get(id=id)
253 replied_post.global_id.to_xml_element(prev_id)
256 replied_post.global_id.to_xml_element(prev_id)
254
257
255
256 next_ids = post.referenced_posts.order_by('id').all()
258 next_ids = post.referenced_posts.order_by('id').all()
257 if len(next_ids) > 0:
259 if len(next_ids) > 0:
258 next_el = et.SubElement(model, TAG_NEXT)
260 next_el = et.SubElement(model, TAG_NEXT)
@@ -260,6 +262,31 b' class PostManager(models.Manager):'
260 next_id = et.SubElement(next_el, TAG_ID)
262 next_id = et.SubElement(next_el, TAG_ID)
261 ref_post.global_id.to_xml_element(next_id)
263 ref_post.global_id.to_xml_element(next_id)
262
264
265 post_signatures = post.signature.all()
266 if post_signatures:
267 signatures[ref_id] = post.signatures
268 else:
269 # TODO Maybe the signature can be computed only once after
270 # the post is added? Need to add some on_save signal queue
271 # and add this there.
272 key = KeyPair.objects.get(public_key=post.global_id.key)
273 signatures[ref_id] = [Signature(
274 key_type=key.key_type,
275 key=key.public_key,
276 signature=key.sign(et.tostring(model, 'unicode')),
277 )]
278 ref_id += 1
279
280 signatures_tag = et.SubElement(response, TAG_SIGNATURES)
281 for ref_id in signatures.keys():
282 signatures = signatures[ref_id]
283
284 for signature in signatures:
285 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
286 signature_tag.set(ATTR_MODEL_REF, str(ref_id))
287 signature_tag.set(ATTR_TYPE, signature.key_type)
288 signature_tag.set(ATTR_VALUE, signature.signature)
289
263 return et.tostring(response, 'unicode')
290 return et.tostring(response, 'unicode')
264
291
265
292
@@ -24,7 +24,7 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.Element):
29 def to_xml_element(self, element: et.Element):
30 """
30 """
@@ -62,6 +62,14 b' class Signature(models.Model):'
62 class Meta:
62 class Meta:
63 app_label = 'boards'
63 app_label = 'boards'
64
64
65 def __init__(self, *args, **kwargs):
66 models.Model.__init__(self, *args, **kwargs)
67
68 if 'key' in kwargs and 'key_type' in kwargs and 'signature' in kwargs:
69 self.key_type = kwargs['key_type']
70 self.key = kwargs['key']
71 self.signature = kwargs['signature']
72
65 key_type = models.TextField()
73 key_type = models.TextField()
66 key = models.TextField()
74 key = models.TextField()
67 signature = models.TextField()
75 signature = models.TextField()
@@ -52,10 +52,10 b' class KeyPair(models.Model):'
52 primary = models.BooleanField(default=False)
52 primary = models.BooleanField(default=False)
53
53
54 def __str__(self):
54 def __str__(self):
55 return '%s | %s' % (self.key_type, self.public_key)
55 return '[%s][%s]' % (self.key_type, self.public_key)
56
56
57 def sign(self, string):
57 def sign(self, string):
58 private = SigningKey.from_string(base64.b64decode(
58 private = SigningKey.from_string(base64.b64decode(
59 self.private_key.encode()))
59 self.private_key.encode()))
60 signature_byte = private.sign(string.encode())
60 signature_byte = private.sign_deterministic(string.encode())
61 return base64.b64encode(signature_byte)
61 return base64.b64encode(signature_byte).decode()
@@ -35,7 +35,8 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 }} </span>
38 <a class="global-id" href="
39 {% url 'post_sync_data' post.id %}"> [RAW] </a>
39 {% endif %}
40 {% endif %}
40
41
41 {% if moderator %}
42 {% if moderator %}
@@ -1,3 +1,4 b''
1 from base64 import b64encode
1 import logging
2 import logging
2
3
3 from django.test import TestCase
4 from django.test import TestCase
@@ -38,11 +39,15 b' class KeyTest(TestCase):'
38 request = Post.objects.generate_request_get([post])
39 request = Post.objects.generate_request_get([post])
39 logger.debug(request)
40 logger.debug(request)
40
41
42 key = KeyPair.objects.get(primary=True)
41 self.assertTrue('<request type="get" version="1.0">'
43 self.assertTrue('<request type="get" version="1.0">'
42 '<model name="post" version="1.0">'
44 '<model name="post" version="1.0">'
43 '<id key="pubkey" local-id="1" type="test_key_type" />'
45 '<id key="%s" local-id="1" type="%s" />'
44 '</model>'
46 '</model>'
45 '</request>' in request,
47 '</request>' % (
48 key.public_key,
49 key.key_type,
50 ) in request,
46 'Wrong XML generated for the GET request.')
51 'Wrong XML generated for the GET request.')
47
52
48 def test_response_get(self):
53 def test_response_get(self):
@@ -58,38 +63,41 b' class KeyTest(TestCase):'
58 response = Post.objects.generate_response_get([reply_post])
63 response = Post.objects.generate_response_get([reply_post])
59 logger.debug(response)
64 logger.debug(response)
60
65
61 self.assertTrue('<response>'
66 key = KeyPair.objects.get(primary=True)
62 '<status>success</status>'
67 self.assertTrue('<status>success</status>'
63 '<models>'
68 '<models>'
64 '<model name="post" ref-id="1">'
69 '<model name="post" ref-id="1">'
65 '<id key="pubkey" local-id="%d" type="test_key_type" />'
70 '<id key="%s" local-id="%d" type="%s" />'
66 '<title>test_title</title>'
71 '<title>test_title</title>'
67 '<text>[post]%d[/post]</text>'
72 '<text>[post]%d[/post]</text>'
68 '<thread>%d</thread>'
73 '<thread>%d</thread>'
69 '<pub-time>%s</pub-time>'
74 '<pub-time>%s</pub-time>'
70 '<edit-time>%s</edit-time>'
75 '<edit-time>%s</edit-time>'
71 '<previous>'
76 '<previous>'
72 '<id key="pubkey" local-id="%d" type="test_key_type" />'
77 '<id key="%s" local-id="%d" type="%s" />'
73 '</previous>'
78 '</previous>'
74 '<next>'
79 '<next>'
75 '<id key="pubkey" local-id="%d" type="test_key_type" />'
80 '<id key="%s" local-id="%d" type="%s" />'
76 '</next>'
81 '</next>'
77 '</model>'
82 '</model>'
78 '</models>'
83 '</models>' % (
79 '</response>' % (
84 key.public_key,
80 reply_post.id,
85 reply_post.id,
86 key.key_type,
81 post.id,
87 post.id,
82 post.id,
88 post.id,
83 str(reply_post.get_edit_time_epoch()),
89 str(reply_post.get_edit_time_epoch()),
84 str(reply_post.get_pub_time_epoch()),
90 str(reply_post.get_pub_time_epoch()),
91 key.public_key,
85 post.id,
92 post.id,
93 key.key_type,
94 key.public_key,
86 reply_reply_post.id,
95 reply_reply_post.id,
96 key.key_type,
87 ) in response,
97 ) in response,
88 'Wrong XML generated for the GET response.')
98 'Wrong XML generated for the GET response.')
89
99
90 def _create_post_with_key(self):
100 def _create_post_with_key(self):
91 key = KeyPair(public_key='pubkey', private_key='privkey',
101 KeyPair.objects.generate_key(primary=True)
92 key_type='test_key_type', primary=True)
93 key.save()
94
102
95 return Post.objects.create_post(title='test_title', text='test_text')
103 return Post.objects.create_post(title='test_title', text='test_text')
@@ -14,10 +14,7 b' class SyncTest(TestCase):'
14 Forms a GET request of a post and checks the response.
14 Forms a GET request of a post and checks the response.
15 """
15 """
16
16
17 key = KeyPair(public_key='pubkey', private_key='privkey',
17 KeyPair.objects.generate_key(primary=True)
18 key_type='test_key_type', primary=True)
19 key.save()
20
21 post = Post.objects.create_post(title='test_title', text='test_text')
18 post = Post.objects.create_post(title='test_title', text='test_text')
22
19
23 request = MockRequest()
20 request = MockRequest()
@@ -32,7 +29,6 b' class SyncTest(TestCase):'
32 )
29 )
33
30
34 self.assertTrue(
31 self.assertTrue(
35 '<response>'
36 '<status>success</status>'
32 '<status>success</status>'
37 '<models>'
33 '<models>'
38 '<model name="post" ref-id="1">'
34 '<model name="post" ref-id="1">'
@@ -42,8 +38,7 b' class SyncTest(TestCase):'
42 '<pub-time>%d</pub-time>'
38 '<pub-time>%d</pub-time>'
43 '<edit-time>%d</edit-time>'
39 '<edit-time>%d</edit-time>'
44 '</model>'
40 '</model>'
45 '</models>'
41 '</models>' % (
46 '</response>' % (
47 post.global_id.key,
42 post.global_id.key,
48 post.id,
43 post.id,
49 post.global_id.key_type,
44 post.global_id.key_type,
@@ -11,6 +11,7 b' from boards.views.search import BoardSea'
11 from boards.views.static import StaticPageView
11 from boards.views.static import StaticPageView
12 from boards.views.post_admin import PostAdminView
12 from boards.views.post_admin import PostAdminView
13 from boards.views.preview import PostPreviewView
13 from boards.views.preview import PostPreviewView
14 from boards.views.sync import get_post_sync_data
14
15
15 js_info_dict = {
16 js_info_dict = {
16 'packages': ('boards',),
17 'packages': ('boards',),
@@ -78,6 +79,9 b" urlpatterns = patterns('',"
78 url(r'^search/$', BoardSearchView.as_view(), name='search'),
79 url(r'^search/$', BoardSearchView.as_view(), name='search'),
79
80
80 # Post preview
81 # Post preview
81 url(r'^preview/$', PostPreviewView.as_view(), name='preview')
82 url(r'^preview/$', PostPreviewView.as_view(), name='preview'),
83
84 url(r'^post_xml/(?P<post_id>\d+)$', get_post_sync_data,
85 name='post_sync_data'),
82
86
83 )
87 )
@@ -1,5 +1,5 b''
1 import xml.etree.ElementTree as et
1 import xml.etree.ElementTree as et
2 from django.http import HttpResponse
2 from django.http import HttpResponse, Http404
3 from boards.models import GlobalId, Post
3 from boards.models import GlobalId, Post
4
4
5
5
@@ -30,4 +30,20 b' def respond_get(request):'
30
30
31 response_xml = Post.objects.generate_response_get(posts)
31 response_xml = Post.objects.generate_response_get(posts)
32
32
33 return HttpResponse(content=response_xml) No newline at end of file
33 return HttpResponse(content=response_xml)
34
35
36 def get_post_sync_data(request, post_id):
37 try:
38 post = Post.objects.get(id=post_id)
39 except Post.DoesNotExist:
40 raise Http404()
41
42 content = 'Global ID: %s\n\nXML: %s' \
43 % (post.global_id, Post.objects.generate_response_get([post]))
44
45
46 return HttpResponse(
47 content_type='text/plain',
48 content=content,
49 ) No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now