##// END OF EJS Templates
Output and parse tags in OP
neko259 -
r1239:7d10156b decentral
parent child Browse files
Show More
@@ -1,164 +1,173 b''
1 1 import xml.etree.ElementTree as et
2 2 from django.db import transaction
3 from boards.models import KeyPair, GlobalId, Signature, Post
3 from boards.models import KeyPair, GlobalId, Signature, Post, Tag
4 4
5 5 ENCODING_UNICODE = 'unicode'
6 6
7 7 TAG_MODEL = 'model'
8 8 TAG_REQUEST = 'request'
9 9 TAG_RESPONSE = 'response'
10 10 TAG_ID = 'id'
11 11 TAG_STATUS = 'status'
12 12 TAG_MODELS = 'models'
13 13 TAG_TITLE = 'title'
14 14 TAG_TEXT = 'text'
15 15 TAG_THREAD = 'thread'
16 16 TAG_PUB_TIME = 'pub-time'
17 17 TAG_SIGNATURES = 'signatures'
18 18 TAG_SIGNATURE = 'signature'
19 19 TAG_CONTENT = 'content'
20 20 TAG_ATTACHMENTS = 'attachments'
21 21 TAG_ATTACHMENT = 'attachment'
22 TAG_TAGS = 'tags'
23 TAG_TAG = 'tag'
22 24
23 25 TYPE_GET = 'get'
24 26
25 27 ATTR_VERSION = 'version'
26 28 ATTR_TYPE = 'type'
27 29 ATTR_NAME = 'name'
28 30 ATTR_VALUE = 'value'
29 31 ATTR_MIMETYPE = 'mimetype'
30 32 ATTR_KEY = 'key'
31 33
32 34 STATUS_SUCCESS = 'success'
33 35
34 36
35 37 class SyncManager:
36 38 @staticmethod
37 39 def generate_response_get(model_list: list):
38 40 response = et.Element(TAG_RESPONSE)
39 41
40 42 status = et.SubElement(response, TAG_STATUS)
41 43 status.text = STATUS_SUCCESS
42 44
43 45 models = et.SubElement(response, TAG_MODELS)
44 46
45 47 for post in model_list:
46 48 model = et.SubElement(models, TAG_MODEL)
47 49 model.set(ATTR_NAME, 'post')
48 50
49 51 content_tag = et.SubElement(model, TAG_CONTENT)
50 52
51 53 tag_id = et.SubElement(content_tag, TAG_ID)
52 54 post.global_id.to_xml_element(tag_id)
53 55
54 56 title = et.SubElement(content_tag, TAG_TITLE)
55 57 title.text = post.title
56 58
57 59 text = et.SubElement(content_tag, TAG_TEXT)
58 60 text.text = post.get_sync_text()
59 61
60 if not post.is_opening():
61 thread = et.SubElement(content_tag, TAG_THREAD)
62 thread_id = et.SubElement(thread, TAG_ID)
63 post.get_thread().get_opening_post().global_id.to_xml_element(thread_id)
62 thread = post.get_thread()
63 if post.is_opening():
64 tag_tags = et.SubElement(content_tag, TAG_TAGS)
65 for tag in thread.get_tags():
66 tag_tag = et.SubElement(tag_tags, TAG_TAG)
67 tag_tag.text = tag.name
64 68 else:
65 # TODO Output tags here
66 pass
69 tag_thread = et.SubElement(content_tag, TAG_THREAD)
70 thread_id = et.SubElement(tag_thread, TAG_ID)
71 thread.get_opening_post().global_id.to_xml_element(thread_id)
67 72
68 73 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
69 74 pub_time.text = str(post.get_pub_time_str())
70 75
71 76 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
72 77 post_signatures = post.signature.all()
73 78 if post_signatures:
74 79 signatures = post_signatures
75 80 # TODO Adding signature to a post is not yet added. For now this
76 81 # block is useless
77 82 else:
78 83 # TODO Maybe the signature can be computed only once after
79 84 # the post is added? Need to add some on_save signal queue
80 85 # and add this there.
81 86 key = KeyPair.objects.get(public_key=post.global_id.key)
82 87 signatures = [Signature(
83 88 key_type=key.key_type,
84 89 key=key.public_key,
85 90 signature=key.sign(et.tostring(content_tag, ENCODING_UNICODE)),
86 91 )]
87 92 for signature in signatures:
88 93 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
89 94 signature_tag.set(ATTR_TYPE, signature.key_type)
90 95 signature_tag.set(ATTR_VALUE, signature.signature)
91 96 signature_tag.set(ATTR_KEY, signature.key)
92 97
93 98 return et.tostring(response, ENCODING_UNICODE)
94 99
95 100 @staticmethod
96 101 @transaction.atomic
97 102 def parse_response_get(response_xml):
98 103 tag_root = et.fromstring(response_xml)
99 104 tag_status = tag_root.find(TAG_STATUS)
100 105 if STATUS_SUCCESS == tag_status.text:
101 106 tag_models = tag_root.find(TAG_MODELS)
102 107 for tag_model in tag_models:
103 108 tag_content = tag_model.find(TAG_CONTENT)
104 109
105 110 valid = SyncManager._verify_model(tag_content, tag_model)
106 111
107 112 if not valid:
108 113 raise Exception('Invalid model signature')
109 114
110 115 tag_id = tag_content.find(TAG_ID)
111 116 global_id, exists = GlobalId.from_xml_element(tag_id)
112 117
113 118 if exists:
114 119 print('Post with same ID already exists')
115 120 else:
116 121 global_id.save()
117 122
118 123 title = tag_content.find(TAG_TITLE).text
119 124 text = tag_content.find(TAG_TEXT).text
120 125 pub_time = tag_content.find(TAG_PUB_TIME).text
121 126
122 127 thread = tag_content.find(TAG_THREAD)
128 tags = []
123 129 if thread:
124 130 opening_post = Post.objects.get(
125 131 id=thread.find(TAG_ID).text)
126 132 else:
127 133 opening_post = None
128 # TODO Get tags here
134 tag_tags = tag_content.find(TAG_TAGS)
135 for tag_tag in tag_tags:
136 tag, created = Tag.objects.get_or_create(name=tag_tag.text)
137 tags.append(tag)
129 138
130 139 # TODO Check that the replied posts are already present
131 140 # before adding new ones
132 141
133 142 # TODO Get images
134 143
135 144 post = Post.objects.import_post(
136 145 title=title, text=text, pub_time=pub_time,
137 opening_post=opening_post)
146 opening_post=opening_post, tags=tags)
138 147 post.global_id = global_id
139 148 else:
140 149 # TODO Throw an exception?
141 150 pass
142 151
143 152 @staticmethod
144 153 def _verify_model(tag_content, tag_model):
145 154 """
146 155 Verifies all signatures for a single model.
147 156 """
148 157
149 158 valid = True
150 159
151 160 tag_signatures = tag_model.find(TAG_SIGNATURES)
152 161 for tag_signature in tag_signatures:
153 162 signature_type = tag_signature.get(ATTR_TYPE)
154 163 signature_value = tag_signature.get(ATTR_VALUE)
155 164 signature_key = tag_signature.get(ATTR_KEY)
156 165
157 166 if not KeyPair.objects.verify(
158 167 signature_key,
159 168 et.tostring(tag_content, ENCODING_UNICODE),
160 169 signature_value, signature_type):
161 170 valid = False
162 171 break
163 172
164 173 return valid
@@ -1,60 +1,69 b''
1 from boards.models import KeyPair, Post
1 from boards.models import KeyPair, Post, Tag
2 2 from boards.models.post.sync import SyncManager
3 3 from boards.tests.mocks import MockRequest
4 4 from boards.views.sync import response_get
5 5
6 6 __author__ = 'neko259'
7 7
8 8
9 9 from django.test import TestCase
10 10
11 11
12 12 class SyncTest(TestCase):
13 13 def test_get(self):
14 14 """
15 15 Forms a GET request of a post and checks the response.
16 16 """
17 17
18 18 KeyPair.objects.generate_key(primary=True)
19 post = Post.objects.create_post(title='test_title', text='test_text')
19 tag = Tag.objects.create(name='tag1')
20 post = Post.objects.create_post(title='test_title', text='test_text',
21 tags=[tag])
20 22
21 23 request = MockRequest()
22 24 request.body = (
23 25 '<request type="get" version="1.0">'
24 26 '<model name="post" version="1.0">'
25 27 '<id key="%s" local-id="%d" type="%s" />'
26 28 '</model>'
27 29 '</request>' % (post.global_id.key,
28 30 post.id,
29 31 post.global_id.key_type)
30 32 )
31 33
32 34 response = response_get(request).content.decode()
33 35 self.assertTrue(
34 36 '<status>success</status>'
35 37 '<models>'
36 38 '<model name="post">'
37 39 '<content>'
38 40 '<id key="%s" local-id="%d" type="%s" />'
39 41 '<title>%s</title>'
40 42 '<text>%s</text>'
43 '<tags><tag>%s</tag></tags>'
41 44 '<pub-time>%s</pub-time>'
42 45 '</content>' % (
43 46 post.global_id.key,
44 47 post.id,
45 48 post.global_id.key_type,
46 49 post.title,
47 50 post.get_raw_text(),
51 post.get_thread().get_tags().first().name,
48 52 post.get_pub_time_str(),
49 53 ) in response_get(request).content.decode(),
50 54 'Wrong response generated for the GET request.')
51 55
52 56 post.delete()
53 57
54 58 SyncManager.parse_response_get(response)
55 59 self.assertEqual(1, Post.objects.count(),
56 60 'Post was not created from XML response.')
57 61
62 parsed_post = Post.objects.first()
63 self.assertEqual('tag1',
64 parsed_post.get_thread().get_tags().first().name,
65 'Invalid tag was parsed.')
66
58 67 SyncManager.parse_response_get(response)
59 68 self.assertEqual(1, Post.objects.count(),
60 69 'The same post was imported twice.')
General Comments 0
You need to be logged in to leave comments. Login now