##// END OF EJS Templates
Save signatures when the post is parsed for the later use
neko259 -
r1244:0f0731ca decentral
parent child Browse files
Show More
@@ -1,128 +1,128 b''
1 from datetime import datetime, timedelta, date
1 from datetime import datetime, timedelta, date
2 from datetime import time as dtime
2 from datetime import time as dtime
3 import logging
3 import logging
4 from django.db import models, transaction
4 from django.db import models, transaction
5 from django.utils import timezone
5 from django.utils import timezone
6 from boards import utils
6 from boards import utils
7 from boards.mdx_neboard import Parser
7 from boards.mdx_neboard import Parser
8 from boards.models import PostImage
8 from boards.models import PostImage
9 import boards.models
9 import boards.models
10
10
11 __author__ = 'vurdalak'
11 __author__ = 'vurdalak'
12
12
13
13
14 NO_IP = '0.0.0.0'
14 NO_IP = '0.0.0.0'
15 POSTS_PER_DAY_RANGE = 7
15 POSTS_PER_DAY_RANGE = 7
16
16
17
17
18 class PostManager(models.Manager):
18 class PostManager(models.Manager):
19 @transaction.atomic
19 @transaction.atomic
20 def create_post(self, title: str, text: str, image=None, thread=None,
20 def create_post(self, title: str, text: str, image=None, thread=None,
21 ip=NO_IP, tags: list=None, opening_posts: list=None):
21 ip=NO_IP, tags: list=None, opening_posts: list=None):
22 """
22 """
23 Creates new post
23 Creates new post
24 """
24 """
25
25
26 is_banned = boards.models.Ban.objects.filter(ip=ip).exists()
26 is_banned = boards.models.Ban.objects.filter(ip=ip).exists()
27
27
28 # TODO Raise specific exception and catch it in the views
28 # TODO Raise specific exception and catch it in the views
29 if is_banned:
29 if is_banned:
30 raise Exception("This user is banned")
30 raise Exception("This user is banned")
31
31
32 if not tags:
32 if not tags:
33 tags = []
33 tags = []
34 if not opening_posts:
34 if not opening_posts:
35 opening_posts = []
35 opening_posts = []
36
36
37 posting_time = timezone.now()
37 posting_time = timezone.now()
38 new_thread = False
38 new_thread = False
39 if not thread:
39 if not thread:
40 thread = boards.models.thread.Thread.objects.create(
40 thread = boards.models.thread.Thread.objects.create(
41 bump_time=posting_time, last_edit_time=posting_time)
41 bump_time=posting_time, last_edit_time=posting_time)
42 list(map(thread.tags.add, tags))
42 list(map(thread.tags.add, tags))
43 boards.models.thread.Thread.objects.process_oldest_threads()
43 boards.models.thread.Thread.objects.process_oldest_threads()
44 new_thread = True
44 new_thread = True
45
45
46 pre_text = Parser().preparse(text)
46 pre_text = Parser().preparse(text)
47
47
48 post = self.create(title=title,
48 post = self.create(title=title,
49 text=pre_text,
49 text=pre_text,
50 pub_time=posting_time,
50 pub_time=posting_time,
51 poster_ip=ip,
51 poster_ip=ip,
52 thread=thread,
52 thread=thread,
53 last_edit_time=posting_time)
53 last_edit_time=posting_time)
54 post.threads.add(thread)
54 post.threads.add(thread)
55
55
56 post.set_global_id()
56 post.set_global_id()
57
57
58 logger = logging.getLogger('boards.post.create')
58 logger = logging.getLogger('boards.post.create')
59
59
60 logger.info('Created post {} by {}'.format(post, post.poster_ip))
60 logger.info('Created post {} by {}'.format(post, post.poster_ip))
61
61
62 if image:
62 if image:
63 post.images.add(PostImage.objects.create_with_hash(image))
63 post.images.add(PostImage.objects.create_with_hash(image))
64
64
65 if not new_thread:
65 if not new_thread:
66 thread.last_edit_time = posting_time
66 thread.last_edit_time = posting_time
67 thread.bump()
67 thread.bump()
68 thread.save()
68 thread.save()
69
69
70 post.build_url()
70 post.build_url()
71 post.connect_replies()
71 post.connect_replies()
72 post.connect_threads(opening_posts)
72 post.connect_threads(opening_posts)
73 post.connect_notifications()
73 post.connect_notifications()
74
74
75 return post
75 return post
76
76
77 @transaction.atomic
77 @transaction.atomic
78 def import_post(self, title: str, text: str, pub_time: str,
78 def import_post(self, title: str, text: str, pub_time: str, global_id,
79 opening_post=None, tags=list()):
79 opening_post=None, tags=list()):
80 if opening_post is None:
80 if opening_post is None:
81 thread = boards.models.thread.Thread.objects.create(
81 thread = boards.models.thread.Thread.objects.create(
82 bump_time=pub_time, last_edit_time=pub_time)
82 bump_time=pub_time, last_edit_time=pub_time)
83 list(map(thread.tags.add, tags))
83 list(map(thread.tags.add, tags))
84 else:
84 else:
85 thread = opening_post.get_thread()
85 thread = opening_post.get_thread()
86
86
87 post = self.create(title=title, text=text,
87 post = self.create(title=title, text=text,
88 pub_time=pub_time,
88 pub_time=pub_time,
89 poster_ip=NO_IP,
89 poster_ip=NO_IP,
90 last_edit_time=pub_time,
90 last_edit_time=pub_time,
91 thread_id=thread.id)
91 thread_id=thread.id, global_id=global_id)
92
92
93 post.build_url()
93 post.build_url()
94 post.connect_replies()
94 post.connect_replies()
95 post.connect_notifications()
95 post.connect_notifications()
96
96
97 return post
97 return post
98
98
99 def delete_posts_by_ip(self, ip):
99 def delete_posts_by_ip(self, ip):
100 """
100 """
101 Deletes all posts of the author with same IP
101 Deletes all posts of the author with same IP
102 """
102 """
103
103
104 posts = self.filter(poster_ip=ip)
104 posts = self.filter(poster_ip=ip)
105 for post in posts:
105 for post in posts:
106 post.delete()
106 post.delete()
107
107
108 @utils.cached_result()
108 @utils.cached_result()
109 def get_posts_per_day(self) -> float:
109 def get_posts_per_day(self) -> float:
110 """
110 """
111 Gets average count of posts per day for the last 7 days
111 Gets average count of posts per day for the last 7 days
112 """
112 """
113
113
114 day_end = date.today()
114 day_end = date.today()
115 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
115 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
116
116
117 day_time_start = timezone.make_aware(datetime.combine(
117 day_time_start = timezone.make_aware(datetime.combine(
118 day_start, dtime()), timezone.get_current_timezone())
118 day_start, dtime()), timezone.get_current_timezone())
119 day_time_end = timezone.make_aware(datetime.combine(
119 day_time_end = timezone.make_aware(datetime.combine(
120 day_end, dtime()), timezone.get_current_timezone())
120 day_end, dtime()), timezone.get_current_timezone())
121
121
122 posts_per_period = float(self.filter(
122 posts_per_period = float(self.filter(
123 pub_time__lte=day_time_end,
123 pub_time__lte=day_time_end,
124 pub_time__gte=day_time_start).count())
124 pub_time__gte=day_time_start).count())
125
125
126 ppd = posts_per_period / POSTS_PER_DAY_RANGE
126 ppd = posts_per_period / POSTS_PER_DAY_RANGE
127
127
128 return ppd
128 return ppd
@@ -1,173 +1,175 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, Tag
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'
22 TAG_TAGS = 'tags'
23 TAG_TAG = 'tag'
23 TAG_TAG = 'tag'
24
24
25 TYPE_GET = 'get'
25 TYPE_GET = 'get'
26
26
27 ATTR_VERSION = 'version'
27 ATTR_VERSION = 'version'
28 ATTR_TYPE = 'type'
28 ATTR_TYPE = 'type'
29 ATTR_NAME = 'name'
29 ATTR_NAME = 'name'
30 ATTR_VALUE = 'value'
30 ATTR_VALUE = 'value'
31 ATTR_MIMETYPE = 'mimetype'
31 ATTR_MIMETYPE = 'mimetype'
32 ATTR_KEY = 'key'
32 ATTR_KEY = 'key'
33
33
34 STATUS_SUCCESS = 'success'
34 STATUS_SUCCESS = 'success'
35
35
36
36
37 class SyncManager:
37 class SyncManager:
38 @staticmethod
38 @staticmethod
39 def generate_response_get(model_list: list):
39 def generate_response_get(model_list: list):
40 response = et.Element(TAG_RESPONSE)
40 response = et.Element(TAG_RESPONSE)
41
41
42 status = et.SubElement(response, TAG_STATUS)
42 status = et.SubElement(response, TAG_STATUS)
43 status.text = STATUS_SUCCESS
43 status.text = STATUS_SUCCESS
44
44
45 models = et.SubElement(response, TAG_MODELS)
45 models = et.SubElement(response, TAG_MODELS)
46
46
47 for post in model_list:
47 for post in model_list:
48 model = et.SubElement(models, TAG_MODEL)
48 model = et.SubElement(models, TAG_MODEL)
49 model.set(ATTR_NAME, 'post')
49 model.set(ATTR_NAME, 'post')
50
50
51 content_tag = et.SubElement(model, TAG_CONTENT)
51 content_tag = et.SubElement(model, TAG_CONTENT)
52
52
53 tag_id = et.SubElement(content_tag, TAG_ID)
53 tag_id = et.SubElement(content_tag, TAG_ID)
54 post.global_id.to_xml_element(tag_id)
54 post.global_id.to_xml_element(tag_id)
55
55
56 title = et.SubElement(content_tag, TAG_TITLE)
56 title = et.SubElement(content_tag, TAG_TITLE)
57 title.text = post.title
57 title.text = post.title
58
58
59 text = et.SubElement(content_tag, TAG_TEXT)
59 text = et.SubElement(content_tag, TAG_TEXT)
60 text.text = post.get_sync_text()
60 text.text = post.get_sync_text()
61
61
62 thread = post.get_thread()
62 thread = post.get_thread()
63 if post.is_opening():
63 if post.is_opening():
64 tag_tags = et.SubElement(content_tag, TAG_TAGS)
64 tag_tags = et.SubElement(content_tag, TAG_TAGS)
65 for tag in thread.get_tags():
65 for tag in thread.get_tags():
66 tag_tag = et.SubElement(tag_tags, TAG_TAG)
66 tag_tag = et.SubElement(tag_tags, TAG_TAG)
67 tag_tag.text = tag.name
67 tag_tag.text = tag.name
68 else:
68 else:
69 tag_thread = et.SubElement(content_tag, TAG_THREAD)
69 tag_thread = et.SubElement(content_tag, TAG_THREAD)
70 thread_id = et.SubElement(tag_thread, TAG_ID)
70 thread_id = et.SubElement(tag_thread, TAG_ID)
71 thread.get_opening_post().global_id.to_xml_element(thread_id)
71 thread.get_opening_post().global_id.to_xml_element(thread_id)
72
72
73 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
73 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
74 pub_time.text = str(post.get_pub_time_str())
74 pub_time.text = str(post.get_pub_time_str())
75
75
76 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
76 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
77 post_signatures = post.global_id.signature_set.all()
77 post_signatures = post.global_id.signature_set.all()
78 if post_signatures:
78 if post_signatures:
79 signatures = post_signatures
79 signatures = post_signatures
80 # 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
81 # block is useless
81 # block is useless
82 else:
82 else:
83 # TODO Maybe the signature can be computed only once after
83 # TODO Maybe the signature can be computed only once after
84 # 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
85 # and add this there.
85 # and add this there.
86 key = KeyPair.objects.get(public_key=post.global_id.key)
86 key = KeyPair.objects.get(public_key=post.global_id.key)
87 signatures = [Signature(
87 signatures = [Signature(
88 key_type=key.key_type,
88 key_type=key.key_type,
89 key=key.public_key,
89 key=key.public_key,
90 signature=key.sign(et.tostring(content_tag, ENCODING_UNICODE)),
90 signature=key.sign(et.tostring(content_tag, ENCODING_UNICODE)),
91 )]
91 )]
92 for signature in signatures:
92 for signature in signatures:
93 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
93 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
94 signature_tag.set(ATTR_TYPE, signature.key_type)
94 signature_tag.set(ATTR_TYPE, signature.key_type)
95 signature_tag.set(ATTR_VALUE, signature.signature)
95 signature_tag.set(ATTR_VALUE, signature.signature)
96 signature_tag.set(ATTR_KEY, signature.key)
96 signature_tag.set(ATTR_KEY, signature.key)
97
97
98 return et.tostring(response, ENCODING_UNICODE)
98 return et.tostring(response, ENCODING_UNICODE)
99
99
100 @staticmethod
100 @staticmethod
101 @transaction.atomic
101 @transaction.atomic
102 def parse_response_get(response_xml):
102 def parse_response_get(response_xml):
103 tag_root = et.fromstring(response_xml)
103 tag_root = et.fromstring(response_xml)
104 tag_status = tag_root.find(TAG_STATUS)
104 tag_status = tag_root.find(TAG_STATUS)
105 if STATUS_SUCCESS == tag_status.text:
105 if STATUS_SUCCESS == tag_status.text:
106 tag_models = tag_root.find(TAG_MODELS)
106 tag_models = tag_root.find(TAG_MODELS)
107 for tag_model in tag_models:
107 for tag_model in tag_models:
108 tag_content = tag_model.find(TAG_CONTENT)
108 tag_content = tag_model.find(TAG_CONTENT)
109
109
110 valid = SyncManager._verify_model(tag_content, tag_model)
110 signatures = SyncManager._verify_model(tag_content, tag_model)
111
112 if not valid:
113 raise Exception('Invalid model signature')
114
111
115 tag_id = tag_content.find(TAG_ID)
112 tag_id = tag_content.find(TAG_ID)
116 global_id, exists = GlobalId.from_xml_element(tag_id)
113 global_id, exists = GlobalId.from_xml_element(tag_id)
117
114
118 if exists:
115 if exists:
119 print('Post with same ID already exists')
116 print('Post with same ID already exists')
120 else:
117 else:
121 global_id.save()
118 global_id.save()
119 for signature in signatures:
120 signature.global_id = global_id
121 signature.save()
122
122
123 title = tag_content.find(TAG_TITLE).text
123 title = tag_content.find(TAG_TITLE).text
124 text = tag_content.find(TAG_TEXT).text
124 text = tag_content.find(TAG_TEXT).text
125 pub_time = tag_content.find(TAG_PUB_TIME).text
125 pub_time = tag_content.find(TAG_PUB_TIME).text
126
126
127 thread = tag_content.find(TAG_THREAD)
127 thread = tag_content.find(TAG_THREAD)
128 tags = []
128 tags = []
129 if thread:
129 if thread:
130 opening_post = Post.objects.get(
130 opening_post = Post.objects.get(
131 id=thread.find(TAG_ID).text)
131 id=thread.find(TAG_ID).text)
132 else:
132 else:
133 opening_post = None
133 opening_post = None
134 tag_tags = tag_content.find(TAG_TAGS)
134 tag_tags = tag_content.find(TAG_TAGS)
135 for tag_tag in tag_tags:
135 for tag_tag in tag_tags:
136 tag, created = Tag.objects.get_or_create(name=tag_tag.text)
136 tag, created = Tag.objects.get_or_create(name=tag_tag.text)
137 tags.append(tag)
137 tags.append(tag)
138
138
139 # TODO Check that the replied posts are already present
139 # TODO Check that the replied posts are already present
140 # before adding new ones
140 # before adding new ones
141
141
142 # TODO Get images
142 # TODO Get images
143
143
144 post = Post.objects.import_post(
144 post = Post.objects.import_post(
145 title=title, text=text, pub_time=pub_time,
145 title=title, text=text, pub_time=pub_time,
146 opening_post=opening_post, tags=tags)
146 opening_post=opening_post, tags=tags,
147 post.global_id = global_id
147 global_id=global_id)
148 else:
148 else:
149 # TODO Throw an exception?
149 # TODO Throw an exception?
150 pass
150 pass
151
151
152 @staticmethod
152 @staticmethod
153 def _verify_model(tag_content, tag_model):
153 def _verify_model(tag_content, tag_model):
154 """
154 """
155 Verifies all signatures for a single model.
155 Verifies all signatures for a single model.
156 """
156 """
157
157
158 valid = True
158 signatures = []
159
159
160 tag_signatures = tag_model.find(TAG_SIGNATURES)
160 tag_signatures = tag_model.find(TAG_SIGNATURES)
161 for tag_signature in tag_signatures:
161 for tag_signature in tag_signatures:
162 signature_type = tag_signature.get(ATTR_TYPE)
162 signature_type = tag_signature.get(ATTR_TYPE)
163 signature_value = tag_signature.get(ATTR_VALUE)
163 signature_value = tag_signature.get(ATTR_VALUE)
164 signature_key = tag_signature.get(ATTR_KEY)
164 signature_key = tag_signature.get(ATTR_KEY)
165
165
166 signature = Signature(key_type=signature_type,
167 key=signature_key,
168 signature=signature_value)
169 signatures.append(signature)
170
166 if not KeyPair.objects.verify(
171 if not KeyPair.objects.verify(
167 signature_key,
172 signature, et.tostring(tag_content, ENCODING_UNICODE)):
168 et.tostring(tag_content, ENCODING_UNICODE),
173 raise Exception('Invalid model signature')
169 signature_value, signature_type):
170 valid = False
171 break
172
174
173 return valid
175 return signatures
@@ -1,61 +1,61 b''
1 import base64
1 import base64
2 from ecdsa import SigningKey, VerifyingKey, BadSignatureError
2 from ecdsa import SigningKey, VerifyingKey, BadSignatureError
3 from django.db import models
3 from django.db import models
4
4
5 TYPE_ECDSA = 'ecdsa'
5 TYPE_ECDSA = 'ecdsa'
6
6
7 APP_LABEL_BOARDS = 'boards'
7 APP_LABEL_BOARDS = 'boards'
8
8
9
9
10 class KeyPairManager(models.Manager):
10 class KeyPairManager(models.Manager):
11 def generate_key(self, key_type=TYPE_ECDSA, primary=False):
11 def generate_key(self, key_type=TYPE_ECDSA, primary=False):
12 if primary and self.filter(primary=True).exists():
12 if primary and self.filter(primary=True).exists():
13 raise Exception('There can be only one primary key')
13 raise Exception('There can be only one primary key')
14
14
15 if key_type == TYPE_ECDSA:
15 if key_type == TYPE_ECDSA:
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 = base64.b64encode(private.to_string()).decode()
19 private_key_str = base64.b64encode(private.to_string()).decode()
20 public_key_str = base64.b64encode(public.to_string()).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,
24 key_type=TYPE_ECDSA, primary=primary)
24 key_type=TYPE_ECDSA, primary=primary)
25 else:
25 else:
26 raise Exception('Key type not supported')
26 raise Exception('Key type not supported')
27
27
28 def verify(self, public_key_str, string, signature, key_type=TYPE_ECDSA):
28 def verify(self, signature, string):
29 if key_type == TYPE_ECDSA:
29 if signature.key_type == TYPE_ECDSA:
30 public = VerifyingKey.from_string(base64.b64decode(public_key_str))
30 public = VerifyingKey.from_string(base64.b64decode(signature.key))
31 signature_byte = base64.b64decode(signature)
31 signature_byte = base64.b64decode(signature.signature)
32 try:
32 try:
33 return public.verify(signature_byte, string.encode())
33 return public.verify(signature_byte, string.encode())
34 except BadSignatureError:
34 except BadSignatureError:
35 return False
35 return False
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):
39 def has_primary(self):
40 return self.filter(primary=True).exists()
40 return self.filter(primary=True).exists()
41
41
42
42
43 class KeyPair(models.Model):
43 class KeyPair(models.Model):
44 class Meta:
44 class Meta:
45 app_label = APP_LABEL_BOARDS
45 app_label = APP_LABEL_BOARDS
46
46
47 objects = KeyPairManager()
47 objects = KeyPairManager()
48
48
49 public_key = models.TextField()
49 public_key = models.TextField()
50 private_key = models.TextField()
50 private_key = models.TextField()
51 key_type = models.TextField()
51 key_type = models.TextField()
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_deterministic(string.encode())
60 signature_byte = private.sign_deterministic(string.encode())
61 return base64.b64encode(signature_byte).decode()
61 return base64.b64encode(signature_byte).decode()
@@ -1,87 +1,89 b''
1 from base64 import b64encode
1 from base64 import b64encode
2 import logging
2 import logging
3
3
4 from django.test import TestCase
4 from django.test import TestCase
5 from boards.models import KeyPair, GlobalId, Post
5 from boards.models import KeyPair, GlobalId, Post, Signature
6 from boards.models.post.sync import SyncManager
6 from boards.models.post.sync import SyncManager
7
7
8 logger = logging.getLogger(__name__)
8 logger = logging.getLogger(__name__)
9
9
10
10
11 class KeyTest(TestCase):
11 class KeyTest(TestCase):
12 def test_create_key(self):
12 def test_create_key(self):
13 key = KeyPair.objects.generate_key('ecdsa')
13 key = KeyPair.objects.generate_key('ecdsa')
14
14
15 self.assertIsNotNone(key, 'The key was not created.')
15 self.assertIsNotNone(key, 'The key was not created.')
16
16
17 def test_validation(self):
17 def test_validation(self):
18 key = KeyPair.objects.generate_key(key_type='ecdsa')
18 key = KeyPair.objects.generate_key(key_type='ecdsa')
19 message = 'msg'
19 message = 'msg'
20 signature = key.sign(message)
20 signature_value = key.sign(message)
21 valid = KeyPair.objects.verify(key.public_key, message, signature,
21
22 key_type='ecdsa')
22 signature = Signature(key_type='ecdsa', key=key.public_key,
23 signature=signature_value)
24 valid = KeyPair.objects.verify(signature, message)
23
25
24 self.assertTrue(valid, 'Message verification failed.')
26 self.assertTrue(valid, 'Message verification failed.')
25
27
26 def test_primary_constraint(self):
28 def test_primary_constraint(self):
27 KeyPair.objects.generate_key(key_type='ecdsa', primary=True)
29 KeyPair.objects.generate_key(key_type='ecdsa', primary=True)
28
30
29 with self.assertRaises(Exception):
31 with self.assertRaises(Exception):
30 KeyPair.objects.generate_key(key_type='ecdsa', primary=True)
32 KeyPair.objects.generate_key(key_type='ecdsa', primary=True)
31
33
32 def test_model_id_save(self):
34 def test_model_id_save(self):
33 model_id = GlobalId(key_type='test', key='test key', local_id='1')
35 model_id = GlobalId(key_type='test', key='test key', local_id='1')
34 model_id.save()
36 model_id.save()
35
37
36 def test_request_get(self):
38 def test_request_get(self):
37 post = self._create_post_with_key()
39 post = self._create_post_with_key()
38
40
39 request = GlobalId.objects.generate_request_get([post.global_id])
41 request = GlobalId.objects.generate_request_get([post.global_id])
40 logger.debug(request)
42 logger.debug(request)
41
43
42 key = KeyPair.objects.get(primary=True)
44 key = KeyPair.objects.get(primary=True)
43 self.assertTrue('<request type="get" version="1.0">'
45 self.assertTrue('<request type="get" version="1.0">'
44 '<model name="post" version="1.0">'
46 '<model name="post" version="1.0">'
45 '<id key="%s" local-id="1" type="%s" />'
47 '<id key="%s" local-id="1" type="%s" />'
46 '</model>'
48 '</model>'
47 '</request>' % (
49 '</request>' % (
48 key.public_key,
50 key.public_key,
49 key.key_type,
51 key.key_type,
50 ) in request,
52 ) in request,
51 'Wrong XML generated for the GET request.')
53 'Wrong XML generated for the GET request.')
52
54
53 def test_response_get(self):
55 def test_response_get(self):
54 post = self._create_post_with_key()
56 post = self._create_post_with_key()
55 reply_post = Post.objects.create_post(title='test_title',
57 reply_post = Post.objects.create_post(title='test_title',
56 text='[post]%d[/post]' % post.id,
58 text='[post]%d[/post]' % post.id,
57 thread=post.get_thread())
59 thread=post.get_thread())
58
60
59 response = SyncManager.generate_response_get([reply_post])
61 response = SyncManager.generate_response_get([reply_post])
60 logger.debug(response)
62 logger.debug(response)
61
63
62 key = KeyPair.objects.get(primary=True)
64 key = KeyPair.objects.get(primary=True)
63 self.assertTrue('<status>success</status>'
65 self.assertTrue('<status>success</status>'
64 '<models>'
66 '<models>'
65 '<model name="post">'
67 '<model name="post">'
66 '<content>'
68 '<content>'
67 '<id key="%s" local-id="%d" type="%s" />'
69 '<id key="%s" local-id="%d" type="%s" />'
68 '<title>test_title</title>'
70 '<title>test_title</title>'
69 '<text>[post]%s[/post]</text>'
71 '<text>[post]%s[/post]</text>'
70 '<thread><id key="%s" local-id="%d" type="%s" /></thread>'
72 '<thread><id key="%s" local-id="%d" type="%s" /></thread>'
71 '<pub-time>%s</pub-time>'
73 '<pub-time>%s</pub-time>'
72 '</content>' % (
74 '</content>' % (
73 key.public_key,
75 key.public_key,
74 reply_post.id,
76 reply_post.id,
75 key.key_type,
77 key.key_type,
76 str(post.global_id),
78 str(post.global_id),
77 key.public_key,
79 key.public_key,
78 post.id,
80 post.id,
79 key.key_type,
81 key.key_type,
80 str(reply_post.get_pub_time_str()),
82 str(reply_post.get_pub_time_str()),
81 ) in response,
83 ) in response,
82 'Wrong XML generated for the GET response.')
84 'Wrong XML generated for the GET response.')
83
85
84 def _create_post_with_key(self):
86 def _create_post_with_key(self):
85 KeyPair.objects.generate_key(primary=True)
87 KeyPair.objects.generate_key(primary=True)
86
88
87 return Post.objects.create_post(title='test_title', text='test_text')
89 return Post.objects.create_post(title='test_title', text='test_text')
@@ -1,69 +1,72 b''
1 from boards.models import KeyPair, Post, Tag
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 tag = Tag.objects.create(name='tag1')
19 tag = Tag.objects.create(name='tag1')
20 post = Post.objects.create_post(title='test_title', text='test_text',
20 post = Post.objects.create_post(title='test_title', text='test_text',
21 tags=[tag])
21 tags=[tag])
22
22
23 request = MockRequest()
23 request = MockRequest()
24 request.body = (
24 request.body = (
25 '<request type="get" version="1.0">'
25 '<request type="get" version="1.0">'
26 '<model name="post" version="1.0">'
26 '<model name="post" version="1.0">'
27 '<id key="%s" local-id="%d" type="%s" />'
27 '<id key="%s" local-id="%d" type="%s" />'
28 '</model>'
28 '</model>'
29 '</request>' % (post.global_id.key,
29 '</request>' % (post.global_id.key,
30 post.id,
30 post.id,
31 post.global_id.key_type)
31 post.global_id.key_type)
32 )
32 )
33
33
34 response = response_get(request).content.decode()
34 response = response_get(request).content.decode()
35 self.assertTrue(
35 self.assertTrue(
36 '<status>success</status>'
36 '<status>success</status>'
37 '<models>'
37 '<models>'
38 '<model name="post">'
38 '<model name="post">'
39 '<content>'
39 '<content>'
40 '<id key="%s" local-id="%d" type="%s" />'
40 '<id key="%s" local-id="%d" type="%s" />'
41 '<title>%s</title>'
41 '<title>%s</title>'
42 '<text>%s</text>'
42 '<text>%s</text>'
43 '<tags><tag>%s</tag></tags>'
43 '<tags><tag>%s</tag></tags>'
44 '<pub-time>%s</pub-time>'
44 '<pub-time>%s</pub-time>'
45 '</content>' % (
45 '</content>' % (
46 post.global_id.key,
46 post.global_id.key,
47 post.id,
47 post.id,
48 post.global_id.key_type,
48 post.global_id.key_type,
49 post.title,
49 post.title,
50 post.get_raw_text(),
50 post.get_raw_text(),
51 post.get_thread().get_tags().first().name,
51 post.get_thread().get_tags().first().name,
52 post.get_pub_time_str(),
52 post.get_pub_time_str(),
53 ) in response_get(request).content.decode(),
53 ) in response_get(request).content.decode(),
54 'Wrong response generated for the GET request.')
54 'Wrong response generated for the GET request.')
55
55
56 post.delete()
56 post.delete()
57
57
58 SyncManager.parse_response_get(response)
58 SyncManager.parse_response_get(response)
59 self.assertEqual(1, Post.objects.count(),
59 self.assertEqual(1, Post.objects.count(),
60 'Post was not created from XML response.')
60 'Post was not created from XML response.')
61
61
62 parsed_post = Post.objects.first()
62 parsed_post = Post.objects.first()
63 self.assertEqual('tag1',
63 self.assertEqual('tag1',
64 parsed_post.get_thread().get_tags().first().name,
64 parsed_post.get_thread().get_tags().first().name,
65 'Invalid tag was parsed.')
65 'Invalid tag was parsed.')
66
66
67 SyncManager.parse_response_get(response)
67 SyncManager.parse_response_get(response)
68 self.assertEqual(1, Post.objects.count(),
68 self.assertEqual(1, Post.objects.count(),
69 'The same post was imported twice.')
69 'The same post was imported twice.')
70
71 self.assertEqual(1, parsed_post.global_id.signature_set.count(),
72 'Signature was not saved.')
General Comments 0
You need to be logged in to leave comments. Login now