diff --git a/activitypub/__init__.py b/activitypub/__init__.py index 91f3aae..45b03e2 100644 --- a/activitypub/__init__.py +++ b/activitypub/__init__.py @@ -1,3 +1,2 @@ -from .manager import Manager from .classes import * from ._version import __version__, VERSION diff --git a/activitypub/bson/objectid.py b/activitypub/bson/objectid.py index 4be91c3..4d63c01 100644 --- a/activitypub/bson/objectid.py +++ b/activitypub/bson/objectid.py @@ -147,7 +147,7 @@ class ObjectId(object): >>> import datetime >>> gen_time = datetime.datetime(2010, 1, 1) >>> early = ObjectId.from_datetime(gen_time) - >>> from activitypub import Manager + >>> from activitypub.manager import Manager >>> from activitypub.database import DummyDatabase >>> m = Manager(database=DummyDatabase()) >>> n = m.Note(_id=early, attributedTo="alyssa") diff --git a/activitypub/classes.py b/activitypub/classes.py index 3376d66..e5a47d6 100644 --- a/activitypub/classes.py +++ b/activitypub/classes.py @@ -46,12 +46,12 @@ class ActivityPubBase(): if attr is None: raise Exception("variable depends on field that is empty: %s" % attr_name) if isinstance(attr, str) and "$" in attr: - setattr(self, attr_name, self.manager.expand_defaults(attr)) + setattr(self, attr_name, self.manager.expand_defaults(attr, self)) def topological_sort(self, data): """ - >>> from activitypub import Manager + >>> from activitypub.manager import Manager >>> manager = Manager() >>> manager.Person(id="alyssa").to_dict() {'@context': 'https://www.w3.org/ns/activitystreams', 'endpoints': {}, 'followers': 'https://example.com/alyssa/followers', 'following': 'https://example.com/alyssa/following', 'id': 'https://example.com/alyssa', 'inbox': 'https://example.com/alyssa/inbox', 'liked': 'https://example.com/alyssa/liked', 'likes': 'https://example.com/alyssa/likes', 'outbox': 'https://example.com/alyssa/outbox', 'type': 'Person', 'url': 'https://example.com/alyssa'} @@ -184,7 +184,7 @@ class Organization(Actor): class Person(Actor): """ - >>> from activitypub import Manager + >>> from activitypub.manager import Manager >>> m = Manager() >>> p = m.Person() >>> p.icon = "image.svg" diff --git a/activitypub/manager/__init__.py b/activitypub/manager/__init__.py new file mode 100644 index 0000000..cb894b0 --- /dev/null +++ b/activitypub/manager/__init__.py @@ -0,0 +1,4 @@ + +from .base import Manager +from .flaskman import FlaskManager +from .tornadoman import TornadoManager diff --git a/activitypub/manager.py b/activitypub/manager/base.py similarity index 77% rename from activitypub/manager.py rename to activitypub/manager/base.py index dbaee1a..cb33be1 100644 --- a/activitypub/manager.py +++ b/activitypub/manager/base.py @@ -1,3 +1,5 @@ +import binascii +import os import uuid class Manager(): @@ -5,7 +7,7 @@ class Manager(): Manager class that ties together ActivityPub objects, defaults, and a database. - >>> from activitypub import Manager + >>> from activitypub.manager import Manager >>> from activitypub.database import DummyDatabase >>> db = DummyDatabase() >>> manager = Manager(database=db) @@ -14,7 +16,7 @@ class Manager(): app_name = "activitypub" version = "1.0.0" def __init__(self, context=None, defaults=None, database=None): - from .classes import ActivityPubBase + from ..classes import ActivityPubBase self.callback = lambda box, activity_id: None self.context = context self.defaults = defaults or self.make_defaults() @@ -61,7 +63,7 @@ class Manager(): self.version, self.expand_defaults("$SCHEME/$HOST")) - def expand_defaults(self, obj, string): + def expand_defaults(self, string, obj=None): """ Expand a string with defaults. """ @@ -71,18 +73,19 @@ class Manager(): string = string.replace(key, self.defaults[key]()) else: string = string.replace(key, self.defaults[key]) - for key in self.parse(string): - if key.startswith("$"): - if getattr(obj, "ap_" + key[1:]) is None: - raise Exception("expansion requires %s" % key[1:]) - string = string.replace(key, getattr(obj, "ap_" + key[1:])) + if obj: + for key in self.parse(string): + if key.startswith("$"): + if getattr(obj, "ap_" + key[1:]) is None: + raise Exception("expansion requires %s" % key[1:]) + string = string.replace(key, getattr(obj, "ap_" + key[1:])) return string def parse(self, string): """ Parse a string delimited by non-alpha, non-$ symbols. - >>> from activitypub import Manager + >>> from activitypub.manager import Manager >>> m = Manager() >>> m.parse("apple/banana/$variable") ['apple', 'banana', '$variable'] @@ -104,7 +107,7 @@ class Manager(): return retval def from_dict(self, data): - from .classes import ActivityPubBase + from ..classes import ActivityPubBase return ActivityPubBase.from_dict(data) def to_list(self, item): @@ -193,3 +196,45 @@ class Manager(): response.raise_for_status() return response.json() + def get_secret_key(self, name): + filename = "%s.key" % name + if not os.path.exists(): + key = binascii.hexlify(os.urandom(32)).decode("utf-8") + with open(filename, "w+") as f: + f.write(key) + return key + else: + with open(key_path) as f: + return f.read() + + def get_rsa_key(self, owner, user, domain): + """" + Loads or generates an RSA key. + """ + k = Key(owner) + user = user.replace(".", "_") + domain = domain.replace(".", "_") + key_path = os.path.join(KEY_DIR, f"key_{user}_{domain}.pem") + if os.path.isfile(key_path): + with open(key_path) as f: + privkey_pem = f.read() + k.load(privkey_pem) + else: + k.new() + with open(key_path, "w") as f: + f.write(k.privkey_pem) + return k + + def after_request(self, f): + """ + Decorator + """ + return f + + def template_filter(self): + """ + Decorator + """ + def decorator(f): + return f + return decorator diff --git a/activitypub/manager/flaskman.py b/activitypub/manager/flaskman.py new file mode 100644 index 0000000..c2cd6b0 --- /dev/null +++ b/activitypub/manager/flaskman.py @@ -0,0 +1,22 @@ +from flask import (Flask, Response) + +from .base import Manager + +class FlaskManager(Manager): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.app = Flask(__name__) + + def after_request(self, f): + """ + Decorator + """ + self.app.after_request(f) + + def template_filter(self): + """ + Decorator + """ + def decorator(f): + self.app.template_filter()(f) + return decorator diff --git a/activitypub/manager/tornadoman.py b/activitypub/manager/tornadoman.py new file mode 100644 index 0000000..8413610 --- /dev/null +++ b/activitypub/manager/tornadoman.py @@ -0,0 +1,6 @@ +from .base import Manager + +class TornadoManager(Manager): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + #self.app = Flask(__name__)