Merge pull request #35 from lbryio/disables-anon

Disables Anonymous Comments
This commit is contained in:
Oleg Silkin 2020-03-03 20:49:42 -05:00 committed by GitHub
commit 464fc88d8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 111 deletions

View file

@ -18,8 +18,7 @@ logger = logging.getLogger(__name__)
def create_comment_or_error(conn, comment, claim_id=None, channel_id=None, channel_name=None, def create_comment_or_error(conn, comment, claim_id=None, channel_id=None, channel_name=None,
signature=None, signing_ts=None, parent_id=None) -> dict: signature=None, signing_ts=None, parent_id=None) -> dict:
if channel_id and channel_name: insert_channel_or_error(conn, channel_name, channel_id)
insert_channel_or_error(conn, channel_name, channel_id)
fn = db.insert_comment if parent_id is None else db.insert_reply fn = db.insert_comment if parent_id is None else db.insert_reply
comment_id = fn( comment_id = fn(
conn=conn, conn=conn,
@ -65,7 +64,7 @@ async def _abandon_comment(app, comment_id): # DELETE
async def create_comment(app, params): async def create_comment(app, params):
if is_valid_base_comment(**params) and is_valid_credential_input(**params): if is_valid_base_comment(**params):
job = await app['comment_scheduler'].spawn(_create_comment(app, params)) job = await app['comment_scheduler'].spawn(_create_comment(app, params))
comment = await job.wait() comment = await job.wait()
if comment: if comment:

View file

@ -50,24 +50,28 @@ def claim_id_is_valid(claim_id: str) -> bool:
return re.fullmatch('([a-z0-9]{40}|[A-Z0-9]{40})', claim_id) is not None return re.fullmatch('([a-z0-9]{40}|[A-Z0-9]{40})', claim_id) is not None
def is_valid_base_comment(comment: str, claim_id: str, parent_id: str = None, **kwargs) -> bool: # default to None so params can be treated as kwargs; param count becomes more manageable
return comment is not None and body_is_valid(comment) and \ def is_valid_base_comment(comment: str = None, claim_id: str = None, parent_id: str = None, **kwargs) -> bool:
((claim_id is not None and claim_id_is_valid(claim_id)) or return comment and body_is_valid(comment) and \
(parent_id is not None and comment_id_is_valid(parent_id))) ((claim_id and claim_id_is_valid(claim_id)) or # parentid is used in place of claimid in replies
(parent_id and comment_id_is_valid(parent_id))) \
and is_valid_credential_input(**kwargs)
def is_valid_credential_input(channel_id: str = None, channel_name: str = None, def is_valid_credential_input(channel_id: str = None, channel_name: str = None,
signature: str = None, signing_ts: str = None, **kwargs) -> bool: signature: str = None, signing_ts: str = None) -> bool:
if channel_id or channel_name or signature or signing_ts: try:
try: assert None not in (channel_id, channel_name, signature, signing_ts)
assert channel_id and channel_name and signature and signing_ts assert is_valid_channel(channel_id, channel_name)
assert is_valid_channel(channel_id, channel_name) assert len(signature) == 128
assert len(signature) == 128 assert signing_ts.isalnum()
assert signing_ts.isalnum()
except Exception: return True
return False
return True except Exception as e:
logger.exception(f'Failed to validate channel: lbry://{channel_name}#{channel_id}, '
f'signature: {signature} signing_ts: {signing_ts}')
return False
def validate_signature_from_claim(claim: dict, signature: typing.Union[str, bytes], def validate_signature_from_claim(claim: dict, signature: typing.Union[str, bytes],

View file

@ -1,4 +1,4 @@
import unittest import sqlite3
from random import randint from random import randint
import faker import faker
@ -53,21 +53,13 @@ class TestDatabaseOperations(DatabaseTestCase):
self.assertEqual(reply['parent_id'], comment['comment_id']) self.assertEqual(reply['parent_id'], comment['comment_id'])
def test02AnonymousComments(self): def test02AnonymousComments(self):
comment = create_comment_or_error( self.assertRaises(
sqlite3.IntegrityError,
create_comment_or_error,
conn=self.conn, conn=self.conn,
claim_id=self.claimId, claim_id=self.claimId,
comment='This is an ANONYMOUS comment' comment='This is an ANONYMOUS comment'
) )
self.assertIsNotNone(comment)
previous_id = comment['comment_id']
reply = create_comment_or_error(
conn=self.conn,
claim_id=self.claimId,
comment='This is an unnamed response',
parent_id=previous_id
)
self.assertIsNotNone(reply)
self.assertEqual(reply['parent_id'], comment['comment_id'])
def test03SignedComments(self): def test03SignedComments(self):
comment = create_comment_or_error( comment = create_comment_or_error(
@ -142,61 +134,7 @@ class TestDatabaseOperations(DatabaseTestCase):
comment='this username is too short' comment='this username is too short'
) )
def test05InsertRandomComments(self): def test05HideComments(self):
# TODO: Fix this test into something practical
self.skipTest('This is a bad test')
top_comments, claim_ids = generate_top_comments_random()
total = 0
success = 0
for _, comments in top_comments.items():
for i, comment in enumerate(comments):
with self.subTest(comment=comment):
result = create_comment_or_error(self.conn, **comment)
if result:
success += 1
comments[i] = result
del comment
total += len(comments)
self.assertLessEqual(success, total)
self.assertGreater(success, 0)
success = 0
for reply in generate_replies_random(top_comments):
reply_id = create_comment_or_error(self.conn, **reply)
if reply_id:
success += 1
self.assertGreater(success, 0)
self.assertLess(success, total)
del top_comments
del claim_ids
def test06GenerateAndListComments(self):
# TODO: Make this test not suck
self.skipTest('this is a stupid test')
top_comments, claim_ids = generate_top_comments()
total, success = 0, 0
for _, comments in top_comments.items():
for i, comment in enumerate(comments):
result = create_comment_or_error(self.conn, **comment)
if result:
success += 1
comments[i] = result
del comment
total += len(comments)
self.assertEqual(total, success)
self.assertGreater(total, 0)
for reply in generate_replies(top_comments):
create_comment_or_error(self.conn, **reply)
for claim_id in claim_ids:
comments_ids = get_comment_ids(self.conn, claim_id)
with self.subTest(comments_ids=comments_ids):
self.assertIs(type(comments_ids), list)
self.assertGreaterEqual(len(comments_ids), 0)
self.assertLessEqual(len(comments_ids), 50)
replies = get_comments_by_id(self.conn, comments_ids)
self.assertLessEqual(len(replies), 50)
self.assertEqual(len(replies), len(comments_ids))
def test07HideComments(self):
comm = create_comment_or_error(self.conn, 'Comment #1', self.claimId, '1'*40, '@Doge123', 'a'*128, '123') comm = create_comment_or_error(self.conn, 'Comment #1', self.claimId, '1'*40, '@Doge123', 'a'*128, '123')
comment = get_comments_by_id(self.conn, [comm['comment_id']]).pop() comment = get_comments_by_id(self.conn, [comm['comment_id']]).pop()
self.assertFalse(comment['is_hidden']) self.assertFalse(comment['is_hidden'])
@ -209,7 +147,7 @@ class TestDatabaseOperations(DatabaseTestCase):
comment = get_comments_by_id(self.conn, [comm['comment_id']]).pop() comment = get_comments_by_id(self.conn, [comm['comment_id']]).pop()
self.assertTrue(comment['is_hidden']) self.assertTrue(comment['is_hidden'])
def test08DeleteComments(self): def test06DeleteComments(self):
comm = create_comment_or_error(self.conn, 'Comment #1', self.claimId, '1'*40, '@Doge123', 'a'*128, '123') comm = create_comment_or_error(self.conn, 'Comment #1', self.claimId, '1'*40, '@Doge123', 'a'*128, '123')
comments = get_claim_comments(self.conn, self.claimId) comments = get_claim_comments(self.conn, self.claimId)
match = list(filter(lambda x: comm['comment_id'] == x['comment_id'], comments['items'])) match = list(filter(lambda x: comm['comment_id'] == x['comment_id'], comments['items']))

View file

@ -11,7 +11,6 @@ from faker.providers import misc
from src.settings import config from src.settings import config
from src.server import app from src.server import app
from src.server.validation import is_valid_channel
from src.server.validation import is_valid_base_comment from src.server.validation import is_valid_base_comment
from test.testcase import AsyncioTestCase from test.testcase import AsyncioTestCase
@ -97,22 +96,6 @@ class ServerTest(AsyncioTestCase):
async def post_comment(self, **params): async def post_comment(self, **params):
return await jsonrpc_post(self.url, 'create_comment', **params) return await jsonrpc_post(self.url, 'create_comment', **params)
@staticmethod
def is_valid_message(comment=None, claim_id=None, parent_id=None,
channel_name=None, channel_id=None, signature=None, signing_ts=None):
try:
assert is_valid_base_comment(comment, claim_id, parent_id)
if channel_name or channel_id or signature or signing_ts:
assert channel_id and channel_name and signature and signing_ts
assert is_valid_channel(channel_id, channel_name)
assert len(signature) == 128
assert signing_ts.isalnum()
except Exception:
return False
return True
async def test01CreateCommentNoReply(self): async def test01CreateCommentNoReply(self):
anonymous_test = create_test_comments( anonymous_test = create_test_comments(
('claim_id', 'channel_id', 'channel_name', 'comment'), ('claim_id', 'channel_id', 'channel_name', 'comment'),
@ -122,13 +105,13 @@ class ServerTest(AsyncioTestCase):
claim_id=None claim_id=None
) )
for test in anonymous_test: for test in anonymous_test:
with self.subTest(test=test): with self.subTest(test='null fields: ' + ', '.join(k for k, v in test.items() if not v)):
message = await self.post_comment(**test) message = await self.post_comment(**test)
self.assertTrue('result' in message or 'error' in message) self.assertTrue('result' in message or 'error' in message)
if 'error' in message: if 'error' in message:
self.assertFalse(self.is_valid_message(**test)) self.assertFalse(is_valid_base_comment(**test))
else: else:
self.assertTrue(self.is_valid_message(**test)) self.assertTrue(is_valid_base_comment(**test))
async def test02CreateNamedCommentsNoReply(self): async def test02CreateNamedCommentsNoReply(self):
named_test = create_test_comments( named_test = create_test_comments(
@ -144,9 +127,9 @@ class ServerTest(AsyncioTestCase):
message = await self.post_comment(**test) message = await self.post_comment(**test)
self.assertTrue('result' in message or 'error' in message) self.assertTrue('result' in message or 'error' in message)
if 'error' in message: if 'error' in message:
self.assertFalse(self.is_valid_message(**test)) self.assertFalse(is_valid_base_comment(**test))
else: else:
self.assertTrue(self.is_valid_message(**test)) self.assertTrue(is_valid_base_comment(**test))
async def test03CreateAllTestComments(self): async def test03CreateAllTestComments(self):
test_all = create_test_comments(replace.keys(), **{ test_all = create_test_comments(replace.keys(), **{
@ -157,9 +140,9 @@ class ServerTest(AsyncioTestCase):
message = await self.post_comment(**test) message = await self.post_comment(**test)
self.assertTrue('result' in message or 'error' in message) self.assertTrue('result' in message or 'error' in message)
if 'error' in message: if 'error' in message:
self.assertFalse(self.is_valid_message(**test)) self.assertFalse(is_valid_base_comment(**test))
else: else:
self.assertTrue(self.is_valid_message(**test)) self.assertTrue(is_valid_base_comment(**test))
async def test04CreateAllReplies(self): async def test04CreateAllReplies(self):
claim_id = '1d8a5cc39ca02e55782d619e67131c0a20843be8' claim_id = '1d8a5cc39ca02e55782d619e67131c0a20843be8'
@ -189,9 +172,37 @@ class ServerTest(AsyncioTestCase):
message = await self.post_comment(**test) message = await self.post_comment(**test)
self.assertTrue('result' in message or 'error' in message) self.assertTrue('result' in message or 'error' in message)
if 'error' in message: if 'error' in message:
self.assertFalse(self.is_valid_message(**test)) self.assertFalse(is_valid_base_comment(**test))
else: else:
self.assertTrue(self.is_valid_message(**test)) self.assertTrue(is_valid_base_comment(**test))
async def testSlackWebhook(self):
claim_id = '1d8a5cc39ca02e55782d619e67131c0a20843be8'
channel_name = '@name'
channel_id = fake.sha1()
signature = '{}'*64
signing_ts = '1234'
base = await self.post_comment(
channel_name=channel_name,
channel_id=channel_id,
comment='duplicate',
claim_id=claim_id,
signing_ts=signing_ts,
signature=signature
)
comment_id = base['result']['comment_id']
with self.subTest(test=comment_id):
await self.post_comment(
channel_name=channel_name,
channel_id=channel_id,
comment='duplicate',
claim_id=claim_id,
signing_ts=signing_ts,
signature=signature
)
class ListCommentsTest(AsyncioTestCase): class ListCommentsTest(AsyncioTestCase):