From fcec33636ca3068a5677aff4d530103c0245b28f Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 14 May 2024 10:54:49 -0400 Subject: [PATCH] verify and recreate client app when login --- boofilsic/settings.py | 2 +- catalog/common/models.py | 4 ++-- catalog/views.py | 4 ++-- journal/models/itemlist.py | 2 +- journal/models/mark.py | 2 +- journal/models/rating.py | 6 +++--- journal/models/utils.py | 7 +++++-- journal/views/collection.py | 2 +- journal/views/review.py | 2 +- mastodon/api.py | 30 +++++++++++++++++++++++++++++- 10 files changed, 46 insertions(+), 15 deletions(-) diff --git a/boofilsic/settings.py b/boofilsic/settings.py index 182d31e1..733998cb 100644 --- a/boofilsic/settings.py +++ b/boofilsic/settings.py @@ -217,7 +217,7 @@ ALLOWED_HOSTS = ["*"] ENABLE_LOCAL_ONLY = env("NEODB_ENABLE_LOCAL_ONLY") # Timeout of requests to Mastodon, in seconds -MASTODON_TIMEOUT = env("NEODB_LOGIN_MASTODON_TIMEOUT", default=10) # type: ignore +MASTODON_TIMEOUT = env("NEODB_LOGIN_MASTODON_TIMEOUT", default=5) # type: ignore TAKAHE_REMOTE_TIMEOUT = MASTODON_TIMEOUT NEODB_USER_AGENT = f"NeoDB/{NEODB_VERSION} (+{SITE_INFO.get('site_url', 'undefined')})" diff --git a/catalog/common/models.py b/catalog/common/models.py index a7712462..6ac38a54 100644 --- a/catalog/common/models.py +++ b/catalog/common/models.py @@ -293,7 +293,7 @@ class Item(SoftDeleteMixin, PolymorphicModel): def history(self): # can't use AuditlogHistoryField bc it will only return history with current content type return LogEntry.objects.filter( - object_id=self.id, content_type_id__in=list(item_content_types().values()) + object_id=self.pk, content_type_id__in=list(item_content_types().values()) ) @cached_property @@ -310,7 +310,7 @@ class Item(SoftDeleteMixin, PolymorphicModel): res.save() def __str__(self): - return f"{self.__class__.__name__}|{self.id}|{self.uuid} {self.primary_lookup_id_type}:{self.primary_lookup_id_value if self.primary_lookup_id_value else ''} ({self.title})" + return f"{self.__class__.__name__}|{self.pk}|{self.uuid} {self.primary_lookup_id_type}:{self.primary_lookup_id_value if self.primary_lookup_id_value else ''} ({self.title})" @classmethod def lookup_id_type_choices(cls): diff --git a/catalog/views.py b/catalog/views.py index b808cd35..e41cd5e3 100644 --- a/catalog/views.py +++ b/catalog/views.py @@ -188,7 +188,7 @@ def review_list(request, item_path, item_uuid): def comments(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) - ids = item.child_item_ids + [item.id] + item.sibling_item_ids + ids = item.child_item_ids + [item.pk] + item.sibling_item_ids queryset = Comment.objects.filter(item_id__in=ids).order_by("-created_time") queryset = queryset.filter(q_piece_visible_to_user(request.user)) before_time = request.GET.get("last") @@ -230,7 +230,7 @@ def comments_by_episode(request, item_path, item_uuid): def reviews(request, item_path, item_uuid): item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid)) - ids = item.child_item_ids + [item.id] + item.sibling_item_ids + ids = item.child_item_ids + [item.pk] + item.sibling_item_ids queryset = Review.objects.filter(item_id__in=ids).order_by("-created_time") queryset = queryset.filter(q_piece_visible_to_user(request.user)) before_time = request.GET.get("last") diff --git a/journal/models/itemlist.py b/journal/models/itemlist.py index bc8f1b14..758cdf40 100644 --- a/journal/models/itemlist.py +++ b/journal/models/itemlist.py @@ -163,4 +163,4 @@ class ListMember(Piece): abstract = True def __str__(self): - return f"{self.__class__.__name__}:{self.id}[{self.parent}]:{self.item}" + return f"{self.__class__.__name__}:{self.pk}[{self.parent}]:{self.item}" diff --git a/journal/models/mark.py b/journal/models/mark.py index e13697ef..beee595a 100644 --- a/journal/models/mark.py +++ b/journal/models/mark.py @@ -47,7 +47,7 @@ class Mark: @property def id(self) -> int | None: - return self.shelfmember.id if self.shelfmember else None + return self.shelfmember.pk if self.shelfmember else None @cached_property def shelf(self) -> Shelf | None: diff --git a/journal/models/rating.py b/journal/models/rating.py index bac1a70b..263b7d55 100644 --- a/journal/models/rating.py +++ b/journal/models/rating.py @@ -75,7 +75,7 @@ class Rating(Content): def get_rating_for_item(item: Item) -> float | None: stat = Rating.objects.filter(grade__isnull=False) if item.class_name in RATING_INCLUDES_CHILD_ITEMS: - stat = stat.filter(item_id__in=item.child_item_ids + [item.id]) + stat = stat.filter(item_id__in=item.child_item_ids + [item.pk]) else: stat = stat.filter(item=item) stat = stat.aggregate(average=Avg("grade"), count=Count("item")) @@ -85,7 +85,7 @@ class Rating(Content): def get_rating_count_for_item(item: Item) -> int: stat = Rating.objects.filter(grade__isnull=False) if item.class_name in RATING_INCLUDES_CHILD_ITEMS: - stat = stat.filter(item_id__in=item.child_item_ids + [item.id]) + stat = stat.filter(item_id__in=item.child_item_ids + [item.pk]) else: stat = stat.filter(item=item) stat = stat.aggregate(count=Count("item")) @@ -95,7 +95,7 @@ class Rating(Content): def get_rating_distribution_for_item(item: Item): stat = Rating.objects.filter(grade__isnull=False) if item.class_name in RATING_INCLUDES_CHILD_ITEMS: - stat = stat.filter(item_id__in=item.child_item_ids + [item.id]) + stat = stat.filter(item_id__in=item.child_item_ids + [item.pk]) else: stat = stat.filter(item=item) stat = stat.values("grade").annotate(count=Count("grade")).order_by("grade") diff --git a/journal/models/utils.py b/journal/models/utils.py index bb109ae7..b0e474a6 100644 --- a/journal/models/utils.py +++ b/journal/models/utils.py @@ -44,6 +44,9 @@ def update_journal_for_merged_item( logger.error("update_journal_for_merged_item: unable to find item") return new_item = legacy_item.merged_to_item + if not new_item: + logger.error("update_journal_for_merged_item: unable to find merged_to_item") + return delete_q = [] for cls in list(Content.__subclasses__()) + list(ListMember.__subclasses__()): for p in cls.objects.filter(item=legacy_item): @@ -54,12 +57,12 @@ def update_journal_for_merged_item( except IntegrityError: if delete_duplicated: logger.warning( - f"deleted piece {p.id} when merging {cls.__name__}: {legacy_item_uuid} -> {new_item.uuid}" + f"deleted piece {p.pk} when merging {cls.__name__}: {legacy_item_uuid} -> {new_item.uuid}" ) delete_q.append(p) else: logger.warning( - f"skip piece {p.id} when merging {cls.__name__}: {legacy_item_uuid} -> {new_item.uuid}" + f"skip piece {p.pk} when merging {cls.__name__}: {legacy_item_uuid} -> {new_item.uuid}" ) for p in delete_q: Debris.create_from_piece(p) diff --git a/journal/views/collection.py b/journal/views/collection.py index d0e5261b..e712ec2a 100644 --- a/journal/views/collection.py +++ b/journal/views/collection.py @@ -36,7 +36,7 @@ def add_to_collection(request: AuthedHttpRequest, item_uuid): cid = Collection.objects.create( owner=request.user.identity, title=_("Collection by {0}").format(request.user.display_name), - ).id + ).pk collection = Collection.objects.get(owner=request.user.identity, id=cid) collection.append_item(item, note=request.POST.get("note")) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) diff --git a/journal/views/review.py b/journal/views/review.py index 62820e6d..3ba773b8 100644 --- a/journal/views/review.py +++ b/journal/views/review.py @@ -50,7 +50,7 @@ def review_edit(request: AuthedHttpRequest, item_uuid, review_uuid=None): if review else ReviewForm( initial={ - "item": item.id, + "item": item.pk, "share_to_mastodon": request.user.preference.mastodon_default_repost, } ) diff --git a/mastodon/api.py b/mastodon/api.py index 3c384bfb..c0efde7a 100644 --- a/mastodon/api.py +++ b/mastodon/api.py @@ -358,7 +358,11 @@ def get_or_create_fediverse_application(login_domain): if not app: app = MastodonApplication.objects.filter(api_domain__iexact=domain).first() if app: - return app + if verify_client(app): + return app + else: + logger.warning(f"Invalid client app for {domain}") + app.delete() if not settings.MASTODON_ALLOW_ANY_SITE: logger.warning(f"Disallowed to create app for {domain}") raise Exception("不支持其它实例登录") @@ -424,6 +428,30 @@ def get_mastodon_login_url(app, login_domain, request): ) +def verify_client(mast_app): + payload = { + "client_id": mast_app.client_id, + "client_secret": mast_app.client_secret, + "redirect_uri": "urn:ietf:wg:oauth:2.0:oob", + "scope": settings.MASTODON_CLIENT_SCOPE, + "grant_type": "client_credentials", + } + headers = {"User-Agent": USER_AGENT} + url = "https://" + (mast_app.api_domain or mast_app.domain_name) + API_OBTAIN_TOKEN + try: + response = post( + url, data=payload, headers=headers, timeout=settings.MASTODON_TIMEOUT + ) + except Exception as e: + logger.warning(f"Error {url} {e}") + return False + if response.status_code != 200: + logger.warning(f"Error {url} {response.status_code}") + return False + data = response.json() + return data.get("access_token") is not None + + def obtain_token(site, request, code): """Returns token if success else None.""" mast_app = MastodonApplication.objects.get(domain_name=site)