Improved audience handling and outbox ALC
parent
4bf54c7040
commit
6d756fb8c4
|
@ -119,7 +119,7 @@ def admin_new(
|
|||
{
|
||||
"in_reply_to_object": in_reply_to_object,
|
||||
"content": content,
|
||||
"visibility_enum": [
|
||||
"visibility_choices": [
|
||||
(v.name, ap.VisibilityEnum.get_display_name(v))
|
||||
for v in ap.VisibilityEnum
|
||||
],
|
||||
|
|
12
app/boxes.py
12
app/boxes.py
|
@ -15,6 +15,7 @@ from app import activitypub as ap
|
|||
from app import config
|
||||
from app import models
|
||||
from app.actor import LOCAL_ACTOR
|
||||
from app.actor import Actor
|
||||
from app.actor import RemoteActor
|
||||
from app.actor import fetch_actor
|
||||
from app.actor import save_actor
|
||||
|
@ -270,6 +271,8 @@ def send_create(
|
|||
elif visibility == ap.VisibilityEnum.DIRECT:
|
||||
to = mentioned_actors
|
||||
cc = []
|
||||
else:
|
||||
raise ValueError(f"Unhandled visibility {visibility}")
|
||||
|
||||
note = {
|
||||
"@context": ap.AS_CTX,
|
||||
|
@ -329,8 +332,7 @@ def _compute_recipients(db: Session, ap_object: ap.RawObject) -> set[str]:
|
|||
|
||||
# If we got a local collection, assume it's a collection of actors
|
||||
if r.startswith(BASE_URL):
|
||||
for raw_actor in fetch_collection(db, r):
|
||||
actor = RemoteActor(raw_actor)
|
||||
for actor in fetch_actor_collection(db, r):
|
||||
recipients.add(actor.shared_inbox_url or actor.inbox_url)
|
||||
|
||||
continue
|
||||
|
@ -757,15 +759,15 @@ def public_outbox_objects_count(db: Session) -> int:
|
|||
)
|
||||
|
||||
|
||||
def fetch_collection(db: Session, url: str) -> list[ap.RawObject]:
|
||||
def fetch_actor_collection(db: Session, url: str) -> list[Actor]:
|
||||
if url.startswith(config.BASE_URL):
|
||||
if url == config.BASE_URL + "/followers":
|
||||
q = db.query(models.Follower).options(joinedload(models.Follower.actor))
|
||||
return [follower.actor.ap_actor for follower in q.all()]
|
||||
return [follower.actor for follower in q.all()]
|
||||
else:
|
||||
raise ValueError(f"internal collection for {url}) not supported")
|
||||
|
||||
return ap.parse_collection(url)
|
||||
return [RemoteActor(actor) for actor in ap.parse_collection(url)]
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
30
app/main.py
30
app/main.py
|
@ -408,14 +408,33 @@ def featured(
|
|||
)
|
||||
|
||||
|
||||
def _check_outbox_object_acl(
|
||||
db: Session, ap_object: models.OutboxObject, httpsig_info: httpsig.HTTPSigInfo
|
||||
) -> None:
|
||||
if ap_object.visibility in [
|
||||
ap.VisibilityEnum.PUBLIC,
|
||||
ap.VisibilityEnum.UNLISTED,
|
||||
]:
|
||||
return None
|
||||
elif ap_object.visibility == ap.VisibilityEnum.FOLLOWERS_ONLY:
|
||||
followers = boxes.fetch_actor_collection(db, BASE_URL + "/followers")
|
||||
if httpsig_info.signed_by_ap_actor_id in [actor.ap_id for actor in followers]:
|
||||
return None
|
||||
elif ap_object.visibility == ap.VisibilityEnum.DIRECT:
|
||||
audience = ap_object.ap_object.get("to", []) + ap_object.ap_object.get("cc", [])
|
||||
if httpsig_info.signed_by_ap_actor_id in audience:
|
||||
return None
|
||||
|
||||
raise HTTPException(status_code=404)
|
||||
|
||||
|
||||
@app.get("/o/{public_id}")
|
||||
def outbox_by_public_id(
|
||||
public_id: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
_: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker),
|
||||
httpsig_info: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker),
|
||||
) -> ActivityPubResponse | templates.TemplateResponse:
|
||||
# TODO: ACL?
|
||||
maybe_object = (
|
||||
db.query(models.OutboxObject)
|
||||
.options(
|
||||
|
@ -432,6 +451,8 @@ def outbox_by_public_id(
|
|||
if not maybe_object:
|
||||
raise HTTPException(status_code=404)
|
||||
|
||||
_check_outbox_object_acl(db, maybe_object, httpsig_info)
|
||||
|
||||
if is_activitypub_requested(request):
|
||||
return ActivityPubResponse(maybe_object.ap_object)
|
||||
|
||||
|
@ -452,9 +473,8 @@ def outbox_by_public_id(
|
|||
def outbox_activity_by_public_id(
|
||||
public_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
_: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker),
|
||||
httpsig_info: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker),
|
||||
) -> ActivityPubResponse:
|
||||
# TODO: ACL?
|
||||
maybe_object = (
|
||||
db.query(models.OutboxObject)
|
||||
.filter(
|
||||
|
@ -466,6 +486,8 @@ def outbox_activity_by_public_id(
|
|||
if not maybe_object:
|
||||
raise HTTPException(status_code=404)
|
||||
|
||||
_check_outbox_object_acl(db, maybe_object, httpsig_info)
|
||||
|
||||
return ActivityPubResponse(ap.wrap_object(maybe_object.ap_object))
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ from loguru import logger
|
|||
from sqlalchemy.orm import Session
|
||||
from starlette.templating import _TemplateResponse as TemplateResponse
|
||||
|
||||
from app import activitypub as ap
|
||||
from app import models
|
||||
from app.actor import LOCAL_ACTOR
|
||||
from app.ap_object import Attachment
|
||||
|
@ -84,6 +85,7 @@ def render_template(
|
|||
"is_admin": is_admin,
|
||||
"csrf_token": generate_csrf_token() if is_admin else None,
|
||||
"highlight_css": HIGHLIGHT_CSS,
|
||||
"visibility_enum": ap.VisibilityEnum,
|
||||
"notifications_count": db.query(models.Notification)
|
||||
.filter(models.Notification.is_new.is_(True))
|
||||
.count()
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
{{ utils.embed_redirect_url() }}
|
||||
<p>
|
||||
<select name="visibility">
|
||||
{% for (k, v) in visibility_enum %}
|
||||
<option value="{{ k }}">{{ v }}</option>
|
||||
{% for (k, v) in visibility_choices %}
|
||||
<option value="{{ k }}" {% if in_reply_to_object and in_reply_to_object.visibility.name == k %}selected{% endif %}>{{ v }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</p>
|
||||
|
|
|
@ -60,12 +60,12 @@
|
|||
</form>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro admin_announce_button(ap_object_id) %}
|
||||
{% macro admin_announce_button(ap_object_id, disabled=False) %}
|
||||
<form action="{{ request.url_for("admin_actions_announce") }}" method="POST">
|
||||
{{ embed_csrf_token() }}
|
||||
{{ embed_redirect_url() }}
|
||||
<input type="hidden" name="ap_object_id" value="{{ ap_object_id }}">
|
||||
<input type="submit" value="Share">
|
||||
<input type="submit" value="Share" {% if disabled %}title="Cannot share non-public content" disabled{% endif %}>
|
||||
</form>
|
||||
{% endmacro %}
|
||||
|
||||
|
@ -253,7 +253,7 @@
|
|||
{% if object.announced_via_outbox_object_ap_id %}
|
||||
{{ admin_undo_button(object.liked_via_outbox_object_ap_id, "Unshare") }}
|
||||
{% else %}
|
||||
{{ admin_announce_button(object.ap_id) }}
|
||||
{{ admin_announce_button(object.ap_id, disabled=object.visibility not in [visibility_enum.PUBLIC, visibility_enum.UNLISTED]) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="bar-item">
|
||||
|
|
Loading…
Reference in New Issue