add scripts/opt_out.py

for #783
pull/804/head
Ryan Barrett 2024-01-27 14:10:51 -08:00
rodzic 9818b39b16
commit ee213531c5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
3 zmienionych plików z 163 dodań i 2 usunięć

3
ids.py
Wyświetl plik

@ -64,7 +64,8 @@ def translate_user_id(*, id, from_proto, to_proto):
str: the corresponding id in ``to_proto``
"""
assert id and from_proto and to_proto
assert from_proto.owns_id(id) is not False or from_proto.LABEL == 'ui'
assert from_proto.owns_id(id) is not False or from_proto.LABEL == 'ui', \
(id, from_proto.LABEL, to_proto.LABEL)
parsed = urlparse(id)
if from_proto.LABEL == 'web' and parsed.path.strip('/') == '':

Wyświetl plik

@ -2,7 +2,7 @@
https://github.com/snarfed/bridgy-fed/issues/286
Run with:
Run from repo top level directory:
source local/bin/activate.csh
env PYTHONPATH=. GOOGLE_APPLICATION_CREDENTIALS=service_account_creds.json \

160
scripts/opt_out.py 100644
Wyświetl plik

@ -0,0 +1,160 @@
"""Opts a user out and deletes their bridged profiles in other networks.
https://github.com/snarfed/bridgy-fed/issues/783
Usage: opt_out.py [PROTOCOL] [USER_ID] [EXTRA_TARGETS ...]
PROTOCOL: protocol label, eg web, activitypub, atproto
USER_ID: key id of the user entity
EXTRA_TARGETS: bridged profiles will also be deleted here
Run with:
source local/bin/activate.csh
env PYTHONPATH=. GOOGLE_APPLICATION_CREDENTIALS=service_account_creds.json \
python scripts/opt_out.py ...
"""
import logging
import sys
from google.cloud import ndb
from oauth_dropins.webutil import appengine_info
appengine_info.DEBUG = False
from oauth_dropins.webutil import appengine_config, flask_util, util
import ids
from models import Object, Target
import protocol
import activitypub, atproto, web
from app import app
appengine_config.error_reporting_client.host = 'localhost:9999'
appengine_config.error_reporting_client.secure = False
# logging.basicConfig(level=logging.DEBUG)
# Includes top 20-40 each from fedidb.org and fediverse.observer on 2024-01-23
AP_BASE_TARGETS = [
# Diaspora
# 'https://joindiaspora.com/',
# 'https://diasp.org/',
# Friendica
'https://venera.social/inbox',
# kbin (not sharedInbox)
'https://kbin.social/i/inbox',
# Lemmy (not sharedInbox)
'https://alien.top/inbox',
# 'https://enterprise.lemmy.ml/inbox',
'https://lemmy.ml/inbox',
'https://lemmy.world/inbox',
'https://pasta.faith/inbox',
# Mastodon
'https://baraag.net/inbox',
'https://c.im/inbox',
'https://daystorm.netz.org/inbox',
'https://fosstodon.org/inbox',
'https://gc2.jp/inbox',
'https://hachyderm.io/inbox',
'https://indieweb.social/inbox',
'https://infosec.exchange/inbox',
'https://mas.to/inbox',
'https://masto.ai/inbox',
'https://mastodon.cloud/inbox',
'https://mastodon.online/inbox',
'https://mastodon.sdf.org/inbox',
'https://mastodon.social/inbox',
'https://mastodon.top/inbox',
'https://mastodon.uno/inbox',
'https://mastodon.world/inbox',
'https://mastodonapp.uk/inbox',
'https://mstdn.jp/inbox',
'https://mstdn.social/inbox',
'https://pawoo.net/inbox',
'https://pravda.me/inbox',
'https://r-sauna.fi/inbox',
'https://techhub.social/inbox',
'https://universeodon.com/inbox',
# Misskey
'https://misskey.io/inbox',
# micro.blog
'https://micro.blog/activitypub/shared/inbox',
# PixelFed (not sharedInbox)
'https://pixelfed.social/i/actor/inbox',
# Twitter bridge
# 'https://bird.makeup/inbox',
]
def run():
assert len(sys.argv) >= 3
proto, user_id, extra_targets = sys.argv[1], sys.argv[2], sys.argv[3:]
# can't do get_by_id because they might be opted out
from_proto = protocol.PROTOCOLS[proto]
kind = from_proto._get_kind()
user = ndb.Key(kind, user_id).get()
assert user, f'{kind} {user_id} not found'
targets = [Target(uri=t, protocol='activitypub')
for t in AP_BASE_TARGETS + extra_targets]
if from_proto is web.Web:
user_id = user.web_url()
to_proto = activitypub.ActivityPub # TODO: generalize
to_user_id = ids.translate_user_id(id=user_id, from_proto=from_proto,
to_proto=to_proto)
delete_id = f'{to_user_id}#bridgy-fed-delete-{util.now().isoformat()}'
obj = Object(id=delete_id, status='new', source_protocol=from_proto.LABEL,
undelivered=targets,
# use as2 so that we don't convert. if we try to convert an opted
# out user's id, we choke. should probably relax that.
as2={
'verb': 'Delete',
'id': delete_id,
# if the actor is already deleted on this instance, it may
# return 502 here because it no longer has the actor's
# public key, so it can't verify the HTTP Sig. (eg Mastodon
# does this; it uses LD Sigs for its actor deletes
# instead.)
#
# an alternative is to use the instance actor:
#
# activitypub.instance_actor().key.urlsafe()
#
# ...which gets accepted, but I'm not sure all
# implementations accept the instance actor as authorized
# to delete a different actor.
'actor': to_user_id,
'object': to_user_id,
})
obj.put()
for target in targets:
assert util.is_web(target.uri), f'Non-URL target: {target}'
params = {
'protocol': to_proto.LABEL,
'url': target.uri,
'obj': obj.key.urlsafe(),
'user': user.key.urlsafe(),
'force': 'true',
}
with app.test_request_context('/queue/send', base_url='https://fed.brid.gy/',
data=params, headers={
flask_util.CLOUD_TASKS_QUEUE_HEADER: '',
}):
protocol.send_task()
with appengine_config.ndb_client.context(), \
app.test_request_context(base_url='https://fed.brid.gy/'):
run()