class TestCommenting(AndreasTestCaseWithKeyPair): def setUpSafe(self): super().setUpSafe() self.post = Post() self.post.server = self.server self.post.path = '/post1' self.post.data = { 'body': 'How are you?', } self.post.save() UserPostRelation.create(source=self.abraham, type='wrote', target=self.post) event = Event() event.server = 'aaa' event.authors = ['bernard@aaa'] event.parent = 'aaa/post1' event.path = '/post1#c1' event.data = {'body': 'Good!'} event.signatures = { 'bernard@aaa': sign_post(event, self.bernard_keypair).hex(), } event.save() process_event(event) def test_comment_created(self): comment = Post.select().join(Server).where( Server.name == 'aaa', Post.path == '/post1#c1').get() PostPostRelation.get(PostPostRelation.source == comment, PostPostRelation.type == 'comments', PostPostRelation.target == self.post)
class TestModifyPost(AndreasTestCaseWithKeyPair): """ With a pre-existing post, try to create/modify/remove three different fields in ``data``. Check that each field was processed correctly and that the fourth field wasn't affected. """ def setUpSafe(self): super().setUpSafe() self.post = Post() self.post.server = self.server self.post.authors = [self.abraham] self.post.path = '/post1' self.post.data = { 'title': 'Hello', 'subtitle': 'A hello world post', 'body': 'Hello, World!', } self.post.save() event = Event() event.server = 'aaa' event.authors = ['abraham@aaa'] event.path = '/post1' event.diff = { 'title': 'Hello (updated)', 'subtitle': None, 'tags': ['Aaa', 'Bbb', 'Ccc'], } event.signatures = { 'abraham@aaa': sign_post(event, self.abraham_keypair, data={ 'title': 'Hello (updated)', 'body': 'Hello, World!', 'tags': ['Aaa', 'Bbb', 'Ccc'], }).hex(), } event.save() process_event(event) self.post.reload() def test_title_updated(self): self.assertEqual('Hello (updated)', self.post.data['title']) def test_subtitle_removed(self): self.assertFalse('subtitle' in self.post.data) def test_body_untouched(self): self.assertEqual('Hello, World!', self.post.data['body']) def test_tags_added(self): self.assertEqual(['Aaa', 'Bbb', 'Ccc'], self.post.data['tags'])
class TestSigningPost(_TestSigning): def setUpSafe(self): super().setUpSafe() self.post = Post() self.post.server = self.server self.post.path = '/post1' self.post.data = {'foo': 'bar', 'bar': 'baz'} self.post.save() UserPostRelation.create(source=self.abraham, type='wrote', target=self.post) def test_sign(self): self.assertEqual(sign_post(self.post, self.abraham_keypair), self.expected) def test_verify(self): verify_post(self.post, 'abraham@aaa', self.expected)
def process_event(event: Event): # Users whose approvals we need for this post required_users: Set[User] = set() # Create or update the post with data provided in this event if event.path: server: Server = Server.get(Server.name == event.server) try: post = Post.get(Post.server == server, Post.path == event.path) except Post.DoesNotExist: post = Post(server=server, path=event.path) # Add/replace elements from the event, # remove elements which are nulls in the information provided by event for key, value in event.diff.items(): if value is not None: post.data[key] = value elif key in post.data: del post.data[key] # Save relations for user_string in event.authors: user = User.from_string(user_string) UserPostRelation.create_after(post, source=user, type='wrote', target=post) required_users.add(user) if event.parent: parent = get_post_by_identifier(event.parent) PostPostRelation.create_after(post, source=post, type='comments', target=parent) verified_signatures: List[Dict] = [] unverified_signatures: List[Dict] = [] verified_users: Set[User] = set() unverified_usernames: Set[str] = set() # Try to verify and save as many signatures as possible for user_string, signature_hex in event.signatures.items(): signature_data = bytes.fromhex(signature_hex) try: keypair = verify_post(post, user_string, signature_data, authors=event.authors) verified_signatures.append( dict(event=event, data=signature_data, keypair=keypair)) verified_users.add(keypair.user) except rsa.VerificationError: unverified_signatures.append( dict(event=event, data=signature_data, user=user_string)) unverified_usernames.add(user_string) # If we got all approvals, then we save post and fill post_id in all the signatures success = False if verified_users >= required_users: success = True post.save() for signature_data in chain(verified_signatures, unverified_signatures): signature_data['post'] = post # If we have already analyzed this event in the past # but had some user's signatures unverified and now some of them became verified, # delete the old unverified instances (UnverifiedSignature.delete().where( UnverifiedSignature.event == event).where( UnverifiedSignature.user << [repr(u) for u in verified_users]).execute()) # Save the signatures # We need them no matter what, even if we are going to raise the exception if verified_signatures: Signature.insert_many(verified_signatures).execute() if unverified_signatures: UnverifiedSignature.insert_many(unverified_signatures).execute() # Raise exception if post was not verified and therefore created/updated if not success: raise UnauthorizedAction(required_users, verified_users, unverified_usernames)