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