log all outgoing actitivies

This commit is contained in:
rimu 2023-12-22 15:34:45 +13:00
parent 4a6492a15c
commit fec2e9117c
7 changed files with 89 additions and 110 deletions

View file

@ -192,6 +192,20 @@ def user_profile(actor):
abort(404)
@bp.route('/u/<actor>/outbox', methods=['GET'])
def user_outbox(actor):
outbox = {
"@context": default_context(),
'type': 'OrderedCollection',
'id': f"https://{current_app.config['SERVER_NAME']}/u/{actor}/outbox",
'orderedItems': [],
'totalItems': 0
}
resp = jsonify(outbox)
resp.content_type = 'application/activity+json'
return resp
@bp.route('/c/<actor>', methods=['GET'])
def community_profile(actor):
""" Requests to this endpoint can be for a JSON representation of the community, or a HTML rendering of it.
@ -483,6 +497,7 @@ def shared_inbox():
community.post_reply_count += 1
community.last_active = utcnow()
post.last_active = utcnow()
post.reply_count += 1
activity_log.result = 'success'
db.session.commit()
else:
@ -915,6 +930,9 @@ def post_ap(post_id):
def activities_json(type, id):
activity = ActivityPubLog.query.filter_by(activity_id=f"https://{current_app.config['SERVER_NAME']}/activities/{type}/{id}").first()
if activity:
...
activity_json = json.loads(activity.activity_json)
resp = jsonify(activity_json)
resp.content_type = 'application/activity+json'
return resp
else:
abort(404)

View file

@ -44,9 +44,10 @@ from datetime import datetime
from dateutil import parser
from pyld import jsonld
from app import db
from app.activitypub.util import default_context
from app.constants import DATETIME_MS_FORMAT
from app.models import utcnow
from app.models import utcnow, ActivityPubLog
def http_date(epoch_seconds=None):
@ -73,6 +74,30 @@ def parse_ld_date(value: str | None) -> datetime | None:
return None
return parser.isoparse(value).replace(microsecond=0)
def post_request(uri: str, body: dict | None, private_key: str, key_id: str, content_type: str = "application/activity+json",
method: Literal["get", "post"] = "post", timeout: int = 5,):
if '@context' not in body: # add a default json-ld context if necessary
body['@context'] = default_context()
log = ActivityPubLog(direction='out', activity_json=json.dumps(body),
result='success', activity_id=body['id'])
try:
result = HttpSignature.signed_request(uri, body, private_key, key_id, content_type, method, timeout)
if result.status_code != 200:
log.result = 'failure'
log.exception_message = f'Response status code was {result.status_code}'
current_app.logger.error('Response code for post attempt was ' +
str(result.status_code) + ' ' + result.text)
except Exception as e:
log.result = 'failure'
log.exception_message='could not send:' + str(e)
current_app.logger.error(f'Exception while sending post to {uri}')
db.session.add(log)
db.session.commit()
return log.result != 'failure'
class VerificationError(BaseException):
"""
There was an error with verifying the signature

View file

@ -1,6 +1,7 @@
from __future__ import annotations
import json
import os
from datetime import datetime
from typing import Union, Tuple
from flask import current_app, request
from sqlalchemy import text
@ -143,36 +144,6 @@ def post_to_activity(post: Post, community: Community):
return activity_data
def validate_headers(headers, body):
if headers['content-type'] != 'application/activity+json' and headers['content-type'] != 'application/ld+json':
return False
if headers['user-agent'] in banned_user_agents():
return False
if instance_blocked(headers['host']):
return False
return validate_header_signature(body, headers['host'], headers['date'], headers['signature'])
def validate_header_signature(body: str, host: str, date: str, signature: str) -> bool:
body = json.loads(body)
signature = parse_signature_header(signature)
key_domain = urlparse(signature['key_id']).hostname
id_domain = urlparse(body['id']).hostname
if urlparse(body['object']['attributedTo']).hostname != key_domain:
raise Exception('Invalid host url.')
if key_domain != id_domain:
raise Exception('Wrong domain.')
user = find_actor_or_create(body['actor'])
return verify_signature(user.private_key, signature, headers)
def banned_user_agents():
return [] # todo: finish this function

View file

@ -4,7 +4,7 @@ from flask_babel import _
from sqlalchemy import or_, desc
from app import db, constants, cache
from app.activitypub.signature import RsaKeys, HttpSignature
from app.activitypub.signature import RsaKeys, HttpSignature, post_request
from app.activitypub.util import default_context
from app.community.forms import SearchRemoteCommunity, AddLocalCommunity, CreatePostForm, ReportCommunityForm, \
DeleteCommunityForm
@ -206,18 +206,12 @@ def subscribe(actor):
"type": "Follow",
"id": f"https://{current_app.config['SERVER_NAME']}/activities/follow/{join_request.id}"
}
try:
message = HttpSignature.signed_request(community.ap_inbox_url, follow, current_user.private_key,
success = post_request(community.ap_inbox_url, follow, current_user.private_key,
current_user.profile_id() + '#main-key')
if message.status_code == 200:
flash('Your request to subscribe has been sent to ' + community.title)
else:
flash('Response status code was not 200', 'warning')
current_app.logger.error('Response code for subscription attempt was ' +
str(message.status_code) + ' ' + message.text)
except Exception as ex:
flash('Failed to send request to subscribe: ' + str(ex), 'error')
current_app.logger.error("Exception while trying to subscribe" + str(ex))
if success:
flash('Your request to subscribe has been sent to ' + community.title)
else:
flash('There was a problem while trying to subscribe.', 'error')
else: # for local communities, joining is instant
banned = CommunityBan.query.filter_by(user_id=current_user.id, community_id=community.id).first()
if banned:
@ -261,18 +255,11 @@ def unsubscribe(actor):
'id': f"https://{current_app.config['SERVER_NAME']}/activities/undo/" + gibberish(15),
'object': follow
}
try:
message = HttpSignature.signed_request(community.ap_inbox_url, undo, current_user.private_key,
success = post_request(community.ap_inbox_url, undo, current_user.private_key,
current_user.profile_id() + '#main-key')
if message.status_code != 200:
flash('Response status code was not 200', 'warning')
current_app.logger.error('Response code for unsubscription attempt was ' +
str(message.status_code) + ' ' + message.text)
proceed = False
except Exception as ex:
proceed = False
flash('Failed to send request to unsubscribe: ' + str(ex), 'error')
current_app.logger.error("Exception while trying to unsubscribe" + str(ex))
if not success:
flash('There was a problem while trying to subscribe', 'error')
if proceed:
db.session.query(CommunityMember).filter_by(user_id=current_user.id, community_id=community.id).delete()
db.session.query(CommunityJoinRequest).filter_by(user_id=current_user.id, community_id=community.id).delete()
@ -361,18 +348,12 @@ def add_post(actor):
"audience": community.ap_profile_id,
"object": page
}
try:
message = HttpSignature.signed_request(community.ap_inbox_url, create, current_user.private_key,
current_user.ap_profile_id + '#main-key')
if message.status_code == 200:
flash('Your post has been sent to ' + community.title)
else:
flash('Response status code was not 200', 'warning')
current_app.logger.error('Response code for post attempt was ' +
str(message.status_code) + ' ' + message.text)
except Exception as ex:
flash('Failed to send request to subscribe: ' + str(ex), 'error')
current_app.logger.error("Exception while trying to subscribe" + str(ex))
success = post_request(community.ap_inbox_url, create, current_user.private_key,
current_user.ap_profile_id + '#main-key')
if success:
flash('Your post has been sent to ' + community.title)
else:
flash('There was a problem sending your post to ' + community.title)
else: # local community - send post out to followers
...

View file

@ -6,7 +6,7 @@ from flask_babel import _
from sqlalchemy import or_, desc
from app import db, constants
from app.activitypub.signature import HttpSignature
from app.activitypub.signature import HttpSignature, post_request
from app.activitypub.util import default_context
from app.community.util import save_post
from app.post.forms import NewReplyForm, ReportPostForm, MeaCulpaForm
@ -104,16 +104,10 @@ def show_post(post_id: int):
'id': f"https://{current_app.config['SERVER_NAME']}/activities/create/{gibberish(15)}"
}
try:
message = HttpSignature.signed_request(post.community.ap_inbox_url, create_json, current_user.private_key,
success = post_request(post.community.ap_inbox_url, create_json, current_user.private_key,
current_user.ap_profile_id + '#main-key')
if message.status_code != 200:
flash('Failed to send to remote instance', 'warning')
current_app.logger.error('Response code for reply attempt was ' +
str(message.status_code) + ' ' + message.text)
except Exception as ex:
flash('Failed to send request to subscribe: ' + str(ex), 'error')
current_app.logger.error("Exception while trying to subscribe" + str(ex))
if not success:
flash('Failed to send to remote instance', 'error')
else: # local community - send it to followers on remote instances
...
@ -191,16 +185,10 @@ def post_vote(post_id: int, vote_direction):
'id': f"https://{current_app.config['SERVER_NAME']}/activities/{action_type.lower()}/{gibberish(15)}",
'audience': post.community.profile_id()
}
try:
message = HttpSignature.signed_request(post.community.ap_inbox_url, action_json, current_user.private_key,
success = post_request(post.community.ap_inbox_url, action_json, current_user.private_key,
current_user.ap_profile_id + '#main-key')
if message.status_code != 200:
flash('Response status code was not 200', 'warning')
current_app.logger.error('Response code for reply attempt was ' +
str(message.status_code) + ' ' + message.text)
except Exception as ex:
flash('Failed to send reply: ' + str(ex), 'error')
current_app.logger.error("Exception while trying to send reply" + str(ex))
if not success:
flash('Failed to send vote', 'warning')
current_user.last_seen = utcnow()
db.session.commit()
@ -266,16 +254,11 @@ def comment_vote(comment_id, vote_direction):
'id': f"https://{current_app.config['SERVER_NAME']}/activities/{action_type.lower()}/{gibberish(15)}",
'audience': comment.community.profile_id()
}
try:
message = HttpSignature.signed_request(comment.community.ap_inbox_url, action_json, current_user.private_key,
success = post_request(comment.community.ap_inbox_url, action_json, current_user.private_key,
current_user.ap_profile_id + '#main-key')
if message.status_code != 200:
flash('Response status code was not 200', 'warning')
current_app.logger.error('Response code for reply attempt was ' +
str(message.status_code) + ' ' + message.text)
except Exception as ex:
flash('Failed to send reply: ' + str(ex), 'error')
current_app.logger.error("Exception while trying to send reply" + str(ex))
if not success:
flash('Failed to send vote', 'error')
current_user.last_seen = utcnow()
db.session.commit()
comment.post.flush_cache()
@ -388,16 +371,10 @@ def add_reply(post_id: int, comment_id: int):
'type': 'Mention'
}
]
try:
message = HttpSignature.signed_request(post.community.ap_inbox_url, create_json, current_user.private_key,
success = post_request(post.community.ap_inbox_url, create_json, current_user.private_key,
current_user.ap_profile_id + '#main-key')
if message.status_code != 200:
flash('Response status code was not 200', 'warning')
current_app.logger.error('Response code for reply attempt was ' +
str(message.status_code) + ' ' + message.text)
except Exception as ex:
flash('Failed to send reply: ' + str(ex), 'error')
current_app.logger.error("Exception while trying to send reply" + str(ex))
if not success:
flash('Failed to send reply', 'error')
else: # local community - send it to followers on remote instances
...
if reply.depth <= constants.THREAD_CUTOFF_DEPTH:

View file

@ -25,9 +25,13 @@
<td>{{ moment(activity.created_at).fromNow() }}</td>
<td>{{ activity.direction }}</td>
<td>{{ activity.activity_id }}</td>
<td>{{ activity.activity_type }}</td>
<td>{{ activity.result }}</td>
<td>{{ activity.exception_message }}</td>
<td>{{ activity.activity_type if activity.activity_type else '' }}</td>
{% if activity.result == 'success' %}
<td><span style="color: green;">{{ activity.result }}</span></td>
{% else %}
<td><span style="color: red;">{{ activity.result }}</span></td>
{% endif %}
<td>{{ activity.exception_message if activity.exception_message else '' }}</td>
<td><pre> </pre></td>
</tr>
{% endfor %}

View file

@ -1,6 +1,8 @@
from __future__ import annotations
import random
from datetime import datetime
from typing import List
from typing import List, Literal
import markdown2
import math
@ -18,7 +20,8 @@ from sqlalchemy import text
from wtforms.fields import SelectField, SelectMultipleField
from wtforms.widgets import Select, html_params, ListWidget, CheckboxInput
from app import db, cache
from app.models import Settings, Domain, Instance, BannedInstances, User, Community, DomainBlock
from app.models import Settings, Domain, Instance, BannedInstances, User, Community, DomainBlock, ActivityPubLog
# Flask's render_template function, with support for themes added