From bcf4d143263f61d78065d8c10cabd10a103a921f Mon Sep 17 00:00:00 2001 From: Thomas Sileo Date: Sat, 16 Jun 2018 21:57:07 +0200 Subject: [PATCH] Cleanup --- .travis.yml | 2 +- little_boxes/__init__.py | 2 +- little_boxes/activitypub.py | 24 +++++++++++++++--------- little_boxes/backend.py | 19 +++++++++++++++++++ little_boxes/content_helper.py | 3 +++ little_boxes/httpsig.py | 2 +- tests/test_backend.py | 10 +--------- 7 files changed, 41 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c307ca..1fb8421 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ install: - pip install -r requirements.txt - pip install -r dev-requirements.txt script: -# - mypy --ignore-missing-imports little_boxes + - mypy --ignore-missing-imports little_boxes - flake8 little_boxes - black --check . - python -m pytest -vv --cov=little_boxes diff --git a/little_boxes/__init__.py b/little_boxes/__init__.py index cdf368d..9b99840 100644 --- a/little_boxes/__init__.py +++ b/little_boxes/__init__.py @@ -3,7 +3,7 @@ import logging logger = logging.getLogger(__name__) -def strtobool(s: str) -> bool: +def strtobool(s: str) -> bool: # pragma: no cover if s in ["y", "yes", "true", "on", "1"]: return True if s in ["n", "no", "false", "off", "0"]: diff --git a/little_boxes/activitypub.py b/little_boxes/activitypub.py index 2908044..eaff440 100644 --- a/little_boxes/activitypub.py +++ b/little_boxes/activitypub.py @@ -367,7 +367,7 @@ class BaseActivity(object, metaclass=_ActivityMeta): actor_id = self._actor_id(actor) return Person(**BACKEND.fetch_iri(actor_id)) - def _pre_post_to_outbox(self) -> None: + def _pre_post_to_outbox(self, as_actor: "Person") -> None: raise NotImplementedError def _post_to_outbox( @@ -407,7 +407,7 @@ class BaseActivity(object, metaclass=_ActivityMeta): ) return - if BACKEND.inbox_get_by_iri(as_actor, self.id): + if BACKEND.inbox_check_duplicate(as_actor, self.id): # The activity is already in the inbox logger.info(f"received duplicate activity {self}, dropping it") return @@ -438,7 +438,7 @@ class BaseActivity(object, metaclass=_ActivityMeta): self.outbox_set_id(BACKEND.activity_url(obj_id), obj_id) try: - self._pre_post_to_outbox() + self._pre_post_to_outbox(self.get_actor()) logger.debug(f"called pre post to outbox hook") except NotImplementedError: logger.debug("pre post to outbox hook not implemented") @@ -671,12 +671,12 @@ class Undo(BaseActivity): except NotImplementedError: pass - def _pre_post_to_outbox(self) -> None: + def _pre_post_to_outbox(self, as_actor: "Person") -> None: """Ensures an Undo activity references an activity owned by the instance.""" if BACKEND is None: raise UninitializedBackendError - if not BACKEND.is_from_outbox(self): + if not BACKEND.is_from_outbox(as_actor, self): raise NotFromOutboxError(f"object {self!r} is not owned by this instance") def _post_to_outbox( @@ -774,6 +774,9 @@ class Announce(BaseActivity): ) return + if BACKEND is None: + raise UninitializedBackendError + BACKEND.inbox_announce(as_actor, self) def _undo_inbox(self, as_actor: "Person") -> None: @@ -789,6 +792,9 @@ class Announce(BaseActivity): activity: ObjectType, recipients: List[str], ) -> None: + if BACKEND is None: + raise UninitializedBackendError + BACKEND.outbox_announce(as_actor, self) def _undo_outbox(self, as_actor: "Person") -> None: @@ -834,14 +840,14 @@ class Delete(BaseActivity): BACKEND.inbox_delete(as_actor, self) # FIXME(tsileo): handle the delete_threads here? - def _pre_post_to_outbox(self) -> None: + def _pre_post_to_outbox(self, as_actor: "Person") -> None: """Ensures the Delete activity references a activity from the outbox (i.e. owned by the instance).""" if BACKEND is None: raise UninitializedBackendError obj = self._get_actual_object() - if not BACKEND.is_from_outbox(self): + if not BACKEND.is_from_outbox(as_actor, self): raise NotFromOutboxError( f'object {obj["id"]} is not owned by this instance' ) @@ -878,11 +884,11 @@ class Update(BaseActivity): BACKEND.inbox_update(as_actor, self) - def _pre_post_to_outbox(self) -> None: + def _pre_post_to_outbox(self, as_actor: "Person") -> None: if BACKEND is None: raise UninitializedBackendError - if not BACKEND.is_from_outbox(self): + if not BACKEND.is_from_outbox(as_actor, self): raise NotFromOutboxError(f"object {self!r} is not owned by this instance") def _post_to_outbox( diff --git a/little_boxes/backend.py b/little_boxes/backend.py index 7b17460..69e3cc2 100644 --- a/little_boxes/backend.py +++ b/little_boxes/backend.py @@ -1,5 +1,7 @@ import abc import typing +import os +import binascii import requests @@ -13,6 +15,10 @@ class Backend(abc.ABC): def user_agent(self) -> str: return f"Little Boxes {__version__} (+http://github.com/tsileo/little-boxes)" + def random_object_id(self) -> str: + """Generates a random object ID.""" + return binascii.hexlify(os.urandom(8)).decode("utf-8") + def fetch_json(self, url: str, **kwargs): resp = requests.get( url, @@ -21,6 +27,15 @@ class Backend(abc.ABC): ) return resp + def is_from_outbox(self, as_actor: "ap.Person", activity: "ap.BaseActivity") -> bool: + return activity.get_actor().id == as_actor.id + + @abc.abstractmethod + def post_to_remote_inbox( + self, as_actor: "ap.Person", payload_encoded: str, recp: str + ) -> None: + pass # pragma: no cover + @abc.abstractmethod def base_url(self) -> str: pass # pragma: no cover @@ -29,6 +44,10 @@ class Backend(abc.ABC): def fetch_iri(self, iri: str) -> "ap.ObjectType": pass # pragma: no cover + @abc.abstractmethod + def inbox_check_duplicate(self, as_actor: "ap.Person", iri: str) -> bool: + pass # pragma: no cover + @abc.abstractmethod def activity_url(self, obj_id: str) -> str: pass # pragma: no cover diff --git a/little_boxes/content_helper.py b/little_boxes/content_helper.py index 4fcc0ac..1e23045 100644 --- a/little_boxes/content_helper.py +++ b/little_boxes/content_helper.py @@ -39,6 +39,9 @@ def mentionify(content: str) -> Tuple[str, List[Dict[str, str]]]: for mention in re.findall(MENTION_REGEX, content): _, username, domain = mention.split("@") actor_url = get_actor_url(mention) + if not actor_url: + # FIXME(tsileo): raise an error? + continue p = get_backend().fetch_iri(actor_url) tags.append(dict(type="Mention", href=p["id"], name=mention)) link = f'@{username}' diff --git a/little_boxes/httpsig.py b/little_boxes/httpsig.py index b982afc..3557620 100644 --- a/little_boxes/httpsig.py +++ b/little_boxes/httpsig.py @@ -55,7 +55,7 @@ def _verify_h(signed_string, signature, pubkey): def _body_digest(body: str) -> str: h = hashlib.new("sha256") - h.update(body) + h.update(body) # type: ignore return "SHA-256=" + base64.b64encode(h.digest()).decode("utf-8") diff --git a/tests/test_backend.py b/tests/test_backend.py index 9b58673..64eb661 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -67,10 +67,6 @@ class InMemBackend(Backend): return calls - def random_object_id(self) -> str: - """Generates a random object ID.""" - return binascii.hexlify(os.urandom(8)).decode("utf-8") - def setup_actor(self, name, pusername): """Create a new actor in this backend.""" p = ap.Person( @@ -126,7 +122,7 @@ class InMemBackend(Backend): return True return False - def inbox_get_by_iri( + def inbox_check_duplicate( self, as_actor: ap.Person, iri: str ) -> Optional[ap.BaseActivity]: for activity in self.DB[as_actor.id]["inbox"]: @@ -194,10 +190,6 @@ class InMemBackend(Backend): as_actor = ap.parse_activity(self.fetch_iri(recp.replace("/inbox", ""))) act.process_from_inbox(as_actor) - def is_from_outbox(self, activity: ap.BaseActivity) -> bool: - # return as_actor.id == activity.get_actor().id - return True # FIXME(tsileo): implement this - @track_call def inbox_like(self, as_actor: ap.Person, activity: ap.Like) -> None: pass