diff --git a/follow.py b/follow.py index 1aefe97..034ec89 100644 --- a/follow.py +++ b/follow.py @@ -33,23 +33,23 @@ def remote_follow(): error(f'Unknown protocol {request.values["protocol"]}') domain = request.values['domain'] - g.user = cls.get_by_id(domain) - if not g.user: + user = cls.get_by_id(domain) + if not user: error(f'No web user found for domain {domain}') addr = request.values['address'] resp = webfinger.fetch(addr) if resp is None: - return redirect(g.user.user_page_path()) + return redirect(user.user_page_path()) for link in resp.get('links', []): if link.get('rel') == webfinger.SUBSCRIBE_LINK_REL: template = link.get('template') if template and '{uri}' in template: - return redirect(template.replace('{uri}', g.user.ap_address())) + return redirect(template.replace('{uri}', user.ap_address())) flash(f"Couldn't find remote follow link for {addr}") - return redirect(g.user.user_page_path()) + return redirect(user.user_page_path()) class FollowStart(indieauth.Start): @@ -89,8 +89,8 @@ class FollowCallback(indieauth.Callback): domain = util.domain_from_link(me) # Web is hard-coded here since this is IndieAuth - g.user = Web.get_by_id(domain) - if not g.user: + user = Web.get_by_id(domain) + if not user: error(f'No web user for domain {domain}') addr = state @@ -100,46 +100,47 @@ class FollowCallback(indieauth.Callback): as2_url = state if util.is_web(state) else webfinger.fetch_actor_url(addr) if not as2_url: flash(f"Couldn't find ActivityPub profile link for {addr}") - return redirect(g.user.user_page_path('following')) + return redirect(user.user_page_path('following')) # TODO(#512): follower will always be Web here, but we should generalize # followee support in UI and here across protocols followee = ActivityPub.load(as2_url) if not followee: flash(f"Couldn't load {as2_url} as AS2") - return redirect(g.user.user_page_path('following')) + return redirect(user.user_page_path('following')) followee_id = followee.as1.get('id') followee_as2 = ActivityPub.convert(followee) inbox = followee_as2.get('inbox') if not followee_id or not inbox: flash(f"AS2 profile {as2_url} missing id or inbox") - return redirect(g.user.user_page_path('following')) + return redirect(user.user_page_path('following')) timestamp = NOW.replace(microsecond=0, tzinfo=None).isoformat() - follow_id = common.host_url(g.user.user_page_path(f'following#{timestamp}-{addr}')) + follow_id = common.host_url(user.user_page_path(f'following#{timestamp}-{addr}')) follow_as2 = { '@context': 'https://www.w3.org/ns/activitystreams', 'type': 'Follow', 'id': follow_id, 'object': followee_id, - 'actor': g.user.ap_actor(), + 'actor': user.ap_actor(), 'to': [as2.PUBLIC_AUDIENCE], } followee_user = ActivityPub.get_or_create(followee_id, obj=followee) - follow_obj = Object(id=follow_id, users=[g.user.key, followee_user.key], + follow_obj = Object(id=follow_id, users=[user.key, followee_user.key], labels=['user'], source_protocol='ui', status='complete', as2=follow_as2) + g.user = user ActivityPub.send(follow_obj, inbox) - Follower.get_or_create(from_=g.user, to=followee_user, status='active', + Follower.get_or_create(from_=user, to=followee_user, status='active', follow=follow_obj.key) follow_obj.put() url = as1.get_url(followee.as1) or followee_id link = common.pretty_link(url, text=addr) flash(f'Followed {link}.') - return redirect(g.user.user_page_path('following')) + return redirect(user.user_page_path('following')) class UnfollowStart(indieauth.Start): @@ -160,7 +161,7 @@ class UnfollowStart(indieauth.Start): except Exception as e: if util.is_connection_failure(e) or util.interpret_http_exception(e)[0]: flash(f"Couldn't fetch your web site: {e}") - return redirect(g.user.user_page_path('following')) + return redirect(user.user_page_path('following')) raise @@ -176,8 +177,8 @@ class UnfollowCallback(indieauth.Callback): domain = util.domain_from_link(me) # Web is hard-coded here since this is IndieAuth - g.user = Web.get_by_id(domain) - if not g.user: + user = Web.get_by_id(domain) + if not user: error(f'No web user for domain {domain}') if util.is_int(state): @@ -200,23 +201,24 @@ class UnfollowCallback(indieauth.Callback): inbox = ActivityPub.convert(followee.obj).get('inbox') if not inbox: flash(f"AS2 profile {followee_id} missing inbox") - return redirect(g.user.user_page_path('following')) + return redirect(user.user_page_path('following')) timestamp = NOW.replace(microsecond=0, tzinfo=None).isoformat() - unfollow_id = common.host_url(g.user.user_page_path(f'following#undo-{timestamp}-{followee_id}')) + unfollow_id = common.host_url(user.user_page_path(f'following#undo-{timestamp}-{followee_id}')) unfollow_as2 = { '@context': 'https://www.w3.org/ns/activitystreams', 'type': 'Undo', 'id': unfollow_id, - 'actor': g.user.ap_actor(), + 'actor': user.ap_actor(), 'object': follower.follow.get().as2 if follower.follow else None, } # don't include the followee User who's being unfollowed in the users # property, since we don't want to notify or show them. (standard social # network etiquette.) - obj = Object(id=unfollow_id, users=[g.user.key], labels=['user'], + obj = Object(id=unfollow_id, users=[user.key], labels=['user'], source_protocol='ui', status='complete', as2=unfollow_as2) + g.user = user ActivityPub.send(obj, inbox) follower.status = 'inactive' @@ -225,7 +227,7 @@ class UnfollowCallback(indieauth.Callback): link = common.pretty_link(as1.get_url(followee.obj.as1) or followee_id) flash(f'Unfollowed {link}.') - return redirect(g.user.user_page_path('following')) + return redirect(user.user_page_path('following')) app.add_url_rule('/follow/start', diff --git a/webfinger.py b/webfinger.py index f675969..3262fc7 100644 --- a/webfinger.py +++ b/webfinger.py @@ -79,27 +79,27 @@ class Webfinger(flask_util.XrdOrJrd): # only allow indirect users if this id is "on" a brid.gy subdomain, # eg user.com@bsky.brid.gy but not user.com@user.com if allow_indirect: - g.user = cls.get_or_create(id) + user = cls.get_or_create(id) else: - g.user = cls.get_by_id(id) - if g.user and not g.user.direct: - error(f"{g.user.key} hasn't signed up yet", status=404) + user = cls.get_by_id(id) + if user and not user.direct: + error(f"{user.key} hasn't signed up yet", status=404) - if not g.user: + if not user: error(f'No {cls.LABEL} user found for {id}', status=404) - actor = g.user.obj.as1 if g.user.obj and g.user.obj.as1 else {} - logger.info(f'Generating WebFinger data for {g.user.key}') + actor = user.obj.as1 if user.obj and user.obj.as1 else {} + logger.info(f'Generating WebFinger data for {user.key}') logger.info(f'AS1 actor: {actor}') urls = util.dedupe_urls(util.get_list(actor, 'urls') + util.get_list(actor, 'url') + - [g.user.web_url()]) + [user.web_url()]) logger.info(f'URLs: {urls}') canonical_url = urls[0] # generate webfinger content data = util.trim_nulls({ - 'subject': 'acct:' + g.user.ap_address().lstrip('@'), + 'subject': 'acct:' + user.ap_address().lstrip('@'), 'aliases': urls, 'links': [{ @@ -126,13 +126,13 @@ class Webfinger(flask_util.XrdOrJrd): # WARNING: in python 2 sometimes request.host_url lost port, # http://localhost:8080 would become just http://localhost. no # clue how or why. pay attention here if that happens again. - 'href': g.user.ap_actor(), + 'href': user.ap_actor(), }, { # AP reads this and sharedInbox from the AS2 actor, not # webfinger, so strictly speaking, it's probably not needed here. 'rel': 'inbox', 'type': as2.CONTENT_TYPE, - 'href': g.user.ap_actor('inbox'), + 'href': user.ap_actor('inbox'), }, { # https://www.w3.org/TR/activitypub/#sharedInbox 'rel': 'sharedInbox', @@ -147,11 +147,11 @@ class Webfinger(flask_util.XrdOrJrd): 'rel': 'http://ostatus.org/schema/1.0/subscribe', # always use fed.brid.gy for UI pages, not protocol subdomain # TODO: switch to: - # 'template': common.host_url(g.user.user_page_path('?url={uri}')), + # 'template': common.host_url(user.user_page_path('?url={uri}')), # the problem is that user_page_path() uses handle_or_id, which uses # custom username instead of domain, which may not be unique 'template': f'https://{common.PRIMARY_DOMAIN}' + - g.user.user_page_path('?url={uri}'), + user.user_page_path('?url={uri}'), }] })