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,
|
"in_reply_to_object": in_reply_to_object,
|
||||||
"content": content,
|
"content": content,
|
||||||
"visibility_enum": [
|
"visibility_choices": [
|
||||||
(v.name, ap.VisibilityEnum.get_display_name(v))
|
(v.name, ap.VisibilityEnum.get_display_name(v))
|
||||||
for v in ap.VisibilityEnum
|
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 config
|
||||||
from app import models
|
from app import models
|
||||||
from app.actor import LOCAL_ACTOR
|
from app.actor import LOCAL_ACTOR
|
||||||
|
from app.actor import Actor
|
||||||
from app.actor import RemoteActor
|
from app.actor import RemoteActor
|
||||||
from app.actor import fetch_actor
|
from app.actor import fetch_actor
|
||||||
from app.actor import save_actor
|
from app.actor import save_actor
|
||||||
|
@ -270,6 +271,8 @@ def send_create(
|
||||||
elif visibility == ap.VisibilityEnum.DIRECT:
|
elif visibility == ap.VisibilityEnum.DIRECT:
|
||||||
to = mentioned_actors
|
to = mentioned_actors
|
||||||
cc = []
|
cc = []
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unhandled visibility {visibility}")
|
||||||
|
|
||||||
note = {
|
note = {
|
||||||
"@context": ap.AS_CTX,
|
"@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 we got a local collection, assume it's a collection of actors
|
||||||
if r.startswith(BASE_URL):
|
if r.startswith(BASE_URL):
|
||||||
for raw_actor in fetch_collection(db, r):
|
for actor in fetch_actor_collection(db, r):
|
||||||
actor = RemoteActor(raw_actor)
|
|
||||||
recipients.add(actor.shared_inbox_url or actor.inbox_url)
|
recipients.add(actor.shared_inbox_url or actor.inbox_url)
|
||||||
|
|
||||||
continue
|
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.startswith(config.BASE_URL):
|
||||||
if url == config.BASE_URL + "/followers":
|
if url == config.BASE_URL + "/followers":
|
||||||
q = db.query(models.Follower).options(joinedload(models.Follower.actor))
|
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:
|
else:
|
||||||
raise ValueError(f"internal collection for {url}) not supported")
|
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
|
@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}")
|
@app.get("/o/{public_id}")
|
||||||
def outbox_by_public_id(
|
def outbox_by_public_id(
|
||||||
public_id: str,
|
public_id: str,
|
||||||
request: Request,
|
request: Request,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
_: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker),
|
httpsig_info: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker),
|
||||||
) -> ActivityPubResponse | templates.TemplateResponse:
|
) -> ActivityPubResponse | templates.TemplateResponse:
|
||||||
# TODO: ACL?
|
|
||||||
maybe_object = (
|
maybe_object = (
|
||||||
db.query(models.OutboxObject)
|
db.query(models.OutboxObject)
|
||||||
.options(
|
.options(
|
||||||
|
@ -432,6 +451,8 @@ def outbox_by_public_id(
|
||||||
if not maybe_object:
|
if not maybe_object:
|
||||||
raise HTTPException(status_code=404)
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
|
_check_outbox_object_acl(db, maybe_object, httpsig_info)
|
||||||
|
|
||||||
if is_activitypub_requested(request):
|
if is_activitypub_requested(request):
|
||||||
return ActivityPubResponse(maybe_object.ap_object)
|
return ActivityPubResponse(maybe_object.ap_object)
|
||||||
|
|
||||||
|
@ -452,9 +473,8 @@ def outbox_by_public_id(
|
||||||
def outbox_activity_by_public_id(
|
def outbox_activity_by_public_id(
|
||||||
public_id: str,
|
public_id: str,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
_: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker),
|
httpsig_info: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker),
|
||||||
) -> ActivityPubResponse:
|
) -> ActivityPubResponse:
|
||||||
# TODO: ACL?
|
|
||||||
maybe_object = (
|
maybe_object = (
|
||||||
db.query(models.OutboxObject)
|
db.query(models.OutboxObject)
|
||||||
.filter(
|
.filter(
|
||||||
|
@ -466,6 +486,8 @@ def outbox_activity_by_public_id(
|
||||||
if not maybe_object:
|
if not maybe_object:
|
||||||
raise HTTPException(status_code=404)
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
|
_check_outbox_object_acl(db, maybe_object, httpsig_info)
|
||||||
|
|
||||||
return ActivityPubResponse(ap.wrap_object(maybe_object.ap_object))
|
return ActivityPubResponse(ap.wrap_object(maybe_object.ap_object))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ from loguru import logger
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from starlette.templating import _TemplateResponse as TemplateResponse
|
from starlette.templating import _TemplateResponse as TemplateResponse
|
||||||
|
|
||||||
|
from app import activitypub as ap
|
||||||
from app import models
|
from app import models
|
||||||
from app.actor import LOCAL_ACTOR
|
from app.actor import LOCAL_ACTOR
|
||||||
from app.ap_object import Attachment
|
from app.ap_object import Attachment
|
||||||
|
@ -84,6 +85,7 @@ def render_template(
|
||||||
"is_admin": is_admin,
|
"is_admin": is_admin,
|
||||||
"csrf_token": generate_csrf_token() if is_admin else None,
|
"csrf_token": generate_csrf_token() if is_admin else None,
|
||||||
"highlight_css": HIGHLIGHT_CSS,
|
"highlight_css": HIGHLIGHT_CSS,
|
||||||
|
"visibility_enum": ap.VisibilityEnum,
|
||||||
"notifications_count": db.query(models.Notification)
|
"notifications_count": db.query(models.Notification)
|
||||||
.filter(models.Notification.is_new.is_(True))
|
.filter(models.Notification.is_new.is_(True))
|
||||||
.count()
|
.count()
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
{{ utils.embed_redirect_url() }}
|
{{ utils.embed_redirect_url() }}
|
||||||
<p>
|
<p>
|
||||||
<select name="visibility">
|
<select name="visibility">
|
||||||
{% for (k, v) in visibility_enum %}
|
{% for (k, v) in visibility_choices %}
|
||||||
<option value="{{ k }}">{{ v }}</option>
|
<option value="{{ k }}" {% if in_reply_to_object and in_reply_to_object.visibility.name == k %}selected{% endif %}>{{ v }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -60,12 +60,12 @@
|
||||||
</form>
|
</form>
|
||||||
{% endmacro %}
|
{% 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">
|
<form action="{{ request.url_for("admin_actions_announce") }}" method="POST">
|
||||||
{{ embed_csrf_token() }}
|
{{ embed_csrf_token() }}
|
||||||
{{ embed_redirect_url() }}
|
{{ embed_redirect_url() }}
|
||||||
<input type="hidden" name="ap_object_id" value="{{ ap_object_id }}">
|
<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>
|
</form>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
@ -253,7 +253,7 @@
|
||||||
{% if object.announced_via_outbox_object_ap_id %}
|
{% if object.announced_via_outbox_object_ap_id %}
|
||||||
{{ admin_undo_button(object.liked_via_outbox_object_ap_id, "Unshare") }}
|
{{ admin_undo_button(object.liked_via_outbox_object_ap_id, "Unshare") }}
|
||||||
{% else %}
|
{% 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 %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="bar-item">
|
<div class="bar-item">
|
||||||
|
|
Loading…
Reference in New Issue