Skip to content

Commit c240fbb

Browse files
amalcaraznesitor
authored andcommitted
fix: amend messages checks
1 parent 7616c2b commit c240fbb

File tree

3 files changed

+445
-18
lines changed

3 files changed

+445
-18
lines changed

src/aleph/handlers/content/post.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
InternalError,
3535
InvalidMessageFormat,
3636
NoAmendTarget,
37+
PermissionDenied,
3738
)
3839

3940
from .content_handler import ContentHandler
@@ -216,6 +217,34 @@ async def check_dependencies(self, session: DbSession, message: MessageDb):
216217
if original_post.type == "amend":
217218
raise CannotAmendAmend()
218219

220+
async def check_permissions(self, session: DbSession, message: MessageDb) -> None:
221+
"""
222+
Check permissions for POST messages.
223+
224+
For amend messages, ensures that the amend message has the same content address
225+
as the original post being amended. This prevents users from amending posts
226+
that don't belong to the same address.
227+
"""
228+
# First, perform the standard authorization check
229+
await super().check_permissions(session=session, message=message)
230+
231+
content = get_post_content(message)
232+
233+
# Additional check for amend messages: ensure same owner
234+
if content.type == "amend":
235+
ref = get_post_content_ref(content.ref)
236+
237+
if ref is not None:
238+
original_post = get_original_post(session=session, item_hash=ref)
239+
240+
if original_post is not None:
241+
# Check that the amend message has the same address as the original post
242+
if original_post.owner != content.address:
243+
raise PermissionDenied(
244+
f"Cannot amend post {ref}: amend message address {content.address} "
245+
f"does not match original post owner {original_post.owner}"
246+
)
247+
219248
async def process_post(self, session: DbSession, message: MessageDb):
220249
content = get_post_content(message)
221250

src/aleph/permissions.py

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
1-
from aleph_message.models import MessageType
1+
from aleph_message.models import MessageType, PostContent
22

33
from aleph.db.accessors.aggregates import get_aggregate_by_key
4+
from aleph.db.accessors.messages import get_message_by_item_hash
45
from aleph.db.models import MessageDb
56
from aleph.types.db_session import DbSession
67

78

8-
async def check_sender_authorization(session: DbSession, message: MessageDb) -> bool:
9-
"""Checks a content against a message to verify if sender is authorized.
10-
11-
TODO: implement "security" aggregate key check.
12-
"""
9+
def _check_delegated_authorization(
10+
session: DbSession, sender: str, owner_address: str, message: MessageDb
11+
) -> bool:
12+
"""Check if sender has delegated authorization for the given owner address.
1313
14-
content = message.parsed_content
14+
Args:
15+
session: Database session
16+
sender: The account trying to perform the action
17+
owner_address: The address that owns the content
18+
message: The message to check permissions against
1519
16-
sender = message.sender
17-
address = content.address
20+
Returns:
21+
True if sender has delegated authorization, False otherwise
22+
"""
1823

19-
# if sender is the content address, all good.
20-
if sender == address:
24+
if sender == owner_address:
2125
return True
2226

2327
aggregate = get_aggregate_by_key(
24-
session=session, key="security", owner=address
25-
) # do we need anything else here?
28+
session=session, key="security", owner=owner_address
29+
)
2630

2731
if not aggregate:
2832
return False
@@ -31,7 +35,7 @@ async def check_sender_authorization(session: DbSession, message: MessageDb) ->
3135

3236
for auth in authorizations:
3337
if auth.get("address", "") != sender:
34-
continue # not applicable, move on.
38+
continue
3539

3640
if auth.get("chain") and message.chain != auth.get("chain"):
3741
continue
@@ -58,3 +62,77 @@ async def check_sender_authorization(session: DbSession, message: MessageDb) ->
5862
return True
5963

6064
return False
65+
66+
67+
async def check_sender_authorization(session: DbSession, message: MessageDb) -> bool:
68+
"""Checks a content against a message to verify if sender is authorized.
69+
70+
For POST messages with type="amend", this function checks permissions against
71+
the original post message instead of the amend message itself. This ensures
72+
that delegated accounts can only amend posts they originally had permission
73+
to create or that were created by accounts they have delegation for.
74+
75+
Special behavior for amend messages:
76+
- If the message is a POST with type="amend" and has a ref to an original post,
77+
the function recursively checks authorization against the original message
78+
- If the original message is not found, it falls back to standard permission checking
79+
- No special "amend" permission is required; if you can post as an address,
80+
you can amend posts from that address
81+
82+
Args:
83+
session: Database session for querying
84+
message: The message to check authorization for
85+
86+
Returns:
87+
True if the sender is authorized, False otherwise
88+
"""
89+
90+
content = message.parsed_content
91+
92+
sender = message.sender
93+
address = content.address
94+
95+
# if sender is the content address, all good.
96+
if sender == address:
97+
return True
98+
99+
# Special handling for POST amend messages
100+
if (
101+
message.type == MessageType.post
102+
and isinstance(content, PostContent)
103+
and content.type == "amend"
104+
):
105+
# For amends, we need to check if the current sender has permissions for the original post's address
106+
if content.ref is not None:
107+
ref_item_hash = (
108+
content.ref.item_hash
109+
if hasattr(content.ref, "item_hash")
110+
else str(content.ref)
111+
)
112+
original_message = get_message_by_item_hash(
113+
session=session, item_hash=ref_item_hash
114+
)
115+
116+
if original_message is not None:
117+
# Create a mock message with the current sender but original message's content address
118+
# This allows us to check if the current sender has permission for the original address
119+
original_content = original_message.parsed_content
120+
if hasattr(original_content, "address"):
121+
# Check permissions for current sender against original post's address
122+
original_address = original_content.address
123+
124+
# Check new owner is the same than the original
125+
if address != original_address:
126+
return False
127+
128+
# Check delegated permissions for original address
129+
return _check_delegated_authorization(
130+
session=session,
131+
sender=sender,
132+
owner_address=original_address,
133+
message=original_message,
134+
)
135+
136+
return _check_delegated_authorization(
137+
session=session, sender=sender, owner_address=address, message=message
138+
)

0 commit comments

Comments
 (0)