Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Simplifications and comments in do_auth #5227

Merged
merged 1 commit into from
May 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/5227.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Simplifications and comments in do_auth.
301 changes: 181 additions & 120 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2013,27 +2013,66 @@ def do_auth(self, origin, event, context, auth_events):

Args:
origin (str):
event (synapse.events.FrozenEvent):
event (synapse.events.EventBase):
context (synapse.events.snapshot.EventContext):
auth_events (dict[(str, str)->str]):
auth_events (dict[(str, str)->synapse.events.EventBase]):
Map from (event_type, state_key) to event

What we expect the event's auth_events to be, based on the event's
position in the dag. I think? maybe??
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we leaving this bit in?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, unless anyone can clarify...


Also NB that this function adds entries to it.
Returns:
defer.Deferred[None]
"""
room_version = yield self.store.get_room_version(event.room_id)

yield self._update_auth_events_and_context_for_auth(
origin, event, context, auth_events
)
try:
self.auth.check(room_version, event, auth_events=auth_events)
except AuthError as e:
logger.warn("Failed auth resolution for %r because %s", event, e)
raise e

@defer.inlineCallbacks
def _update_auth_events_and_context_for_auth(
self, origin, event, context, auth_events
):
"""Helper for do_auth. See there for docs.

Args:
origin (str):
event (synapse.events.EventBase):
context (synapse.events.snapshot.EventContext):
auth_events (dict[(str, str)->synapse.events.EventBase]):

Returns:
defer.Deferred[None]
"""
# Check if we have all the auth events.
current_state = set(e.event_id for e in auth_events.values())
event_auth_events = set(event.auth_event_ids())

if event.is_state():
event_key = (event.type, event.state_key)
else:
event_key = None

if event_auth_events - current_state:
# if the event's auth_events refers to events which are not in our
# calculated auth_events, we need to fetch those events from somewhere.
#
# we start by fetching them from the store, and then try calling /event_auth/.
missing_auth = event_auth_events.difference(
e.event_id for e in auth_events.values()
)

if missing_auth:
# TODO: can we use store.have_seen_events here instead?
have_events = yield self.store.get_seen_events_with_rejections(
event_auth_events - current_state
missing_auth
)
logger.debug("Got events %s from store", have_events)
missing_auth.difference_update(have_events.keys())
else:
have_events = {}

Expand All @@ -2042,13 +2081,12 @@ def do_auth(self, origin, event, context, auth_events):
for e in auth_events.values()
})

seen_events = set(have_events.keys())

missing_auth = event_auth_events - seen_events - current_state

if missing_auth:
logger.info("Missing auth: %s", missing_auth)
# If we don't have all the auth events, we need to get them.
logger.info(
"auth_events contains unknown events: %s",
missing_auth,
)
try:
remote_auth_chain = yield self.federation_client.get_event_auth(
origin, event.room_id, event.event_id
Expand Down Expand Up @@ -2089,145 +2127,168 @@ def do_auth(self, origin, event, context, auth_events):
have_events = yield self.store.get_seen_events_with_rejections(
event.auth_event_ids()
)
seen_events = set(have_events.keys())
except Exception:
# FIXME:
logger.exception("Failed to get auth chain")

if event.internal_metadata.is_outlier():
logger.info("Skipping auth_event fetch for outlier")
return

# FIXME: Assumes we have and stored all the state for all the
# prev_events
current_state = set(e.event_id for e in auth_events.values())
different_auth = event_auth_events - current_state
different_auth = event_auth_events.difference(
e.event_id for e in auth_events.values()
)

room_version = yield self.store.get_room_version(event.room_id)
if not different_auth:
return

if different_auth and not event.internal_metadata.is_outlier():
# Do auth conflict res.
logger.info("Different auth: %s", different_auth)

different_events = yield logcontext.make_deferred_yieldable(
defer.gatherResults([
logcontext.run_in_background(
self.store.get_event,
d,
allow_none=True,
allow_rejected=False,
)
for d in different_auth
if d in have_events and not have_events[d]
], consumeErrors=True)
).addErrback(unwrapFirstError)

if different_events:
local_view = dict(auth_events)
remote_view = dict(auth_events)
remote_view.update({
(d.type, d.state_key): d for d in different_events if d
})
logger.info(
"auth_events refers to events which are not in our calculated auth "
"chain: %s",
different_auth,
)

room_version = yield self.store.get_room_version(event.room_id)

new_state = yield self.state_handler.resolve_events(
room_version,
[list(local_view.values()), list(remote_view.values())],
event
different_events = yield logcontext.make_deferred_yieldable(
defer.gatherResults([
logcontext.run_in_background(
self.store.get_event,
d,
allow_none=True,
allow_rejected=False,
)
for d in different_auth
if d in have_events and not have_events[d]
], consumeErrors=True)
).addErrback(unwrapFirstError)

if different_events:
local_view = dict(auth_events)
remote_view = dict(auth_events)
remote_view.update({
(d.type, d.state_key): d for d in different_events if d
})

auth_events.update(new_state)
new_state = yield self.state_handler.resolve_events(
room_version,
[list(local_view.values()), list(remote_view.values())],
event
)

current_state = set(e.event_id for e in auth_events.values())
different_auth = event_auth_events - current_state
logger.info(
"After state res: updating auth_events with new state %s",
{
(d.type, d.state_key): d.event_id for d in new_state.values()
if auth_events.get((d.type, d.state_key)) != d
},
)

yield self._update_context_for_auth_events(
event, context, auth_events, event_key,
)
auth_events.update(new_state)

different_auth = event_auth_events.difference(
e.event_id for e in auth_events.values()
)

if different_auth and not event.internal_metadata.is_outlier():
logger.info("Different auth after resolution: %s", different_auth)
yield self._update_context_for_auth_events(
event, context, auth_events, event_key,
)

# Only do auth resolution if we have something new to say.
# We can't rove an auth failure.
do_resolution = False
if not different_auth:
# we're done
return

provable = [
RejectedReason.NOT_ANCESTOR, RejectedReason.NOT_ANCESTOR,
]
logger.info(
"auth_events still refers to events which are not in the calculated auth "
"chain after state resolution: %s",
different_auth,
)

for e_id in different_auth:
if e_id in have_events:
if have_events[e_id] in provable:
do_resolution = True
break
# Only do auth resolution if we have something new to say.
# We can't prove an auth failure.
do_resolution = False

if do_resolution:
prev_state_ids = yield context.get_prev_state_ids(self.store)
# 1. Get what we think is the auth chain.
auth_ids = yield self.auth.compute_auth_events(
event, prev_state_ids
)
local_auth_chain = yield self.store.get_auth_chain(
auth_ids, include_given=True
)
for e_id in different_auth:
if e_id in have_events:
if have_events[e_id] == RejectedReason.NOT_ANCESTOR:
do_resolution = True
break

try:
# 2. Get remote difference.
result = yield self.federation_client.query_auth(
origin,
event.room_id,
event.event_id,
local_auth_chain,
)
if not do_resolution:
logger.info(
"Skipping auth resolution due to lack of provable rejection reasons"
)
return

seen_remotes = yield self.store.have_seen_events(
[e.event_id for e in result["auth_chain"]]
)
logger.info("Doing auth resolution")

# 3. Process any remote auth chain events we haven't seen.
for ev in result["auth_chain"]:
if ev.event_id in seen_remotes:
continue
prev_state_ids = yield context.get_prev_state_ids(self.store)

if ev.event_id == event.event_id:
continue
# 1. Get what we think is the auth chain.
auth_ids = yield self.auth.compute_auth_events(
event, prev_state_ids
)
local_auth_chain = yield self.store.get_auth_chain(
auth_ids, include_given=True
)

try:
auth_ids = ev.auth_event_ids()
auth = {
(e.type, e.state_key): e
for e in result["auth_chain"]
if e.event_id in auth_ids
or event.type == EventTypes.Create
}
ev.internal_metadata.outlier = True
try:
# 2. Get remote difference.
result = yield self.federation_client.query_auth(
origin,
event.room_id,
event.event_id,
local_auth_chain,
)

logger.debug(
"do_auth %s different_auth: %s",
event.event_id, e.event_id
)
seen_remotes = yield self.store.have_seen_events(
[e.event_id for e in result["auth_chain"]]
)

yield self._handle_new_event(
origin, ev, auth_events=auth
)
# 3. Process any remote auth chain events we haven't seen.
for ev in result["auth_chain"]:
if ev.event_id in seen_remotes:
continue

if ev.event_id in event_auth_events:
auth_events[(ev.type, ev.state_key)] = ev
except AuthError:
pass
if ev.event_id == event.event_id:
continue

except Exception:
# FIXME:
logger.exception("Failed to query auth chain")
try:
auth_ids = ev.auth_event_ids()
auth = {
(e.type, e.state_key): e
for e in result["auth_chain"]
if e.event_id in auth_ids
or event.type == EventTypes.Create
}
ev.internal_metadata.outlier = True

logger.debug(
"do_auth %s different_auth: %s",
event.event_id, e.event_id
)

# 4. Look at rejects and their proofs.
# TODO.
yield self._handle_new_event(
origin, ev, auth_events=auth
)

yield self._update_context_for_auth_events(
event, context, auth_events, event_key,
)
if ev.event_id in event_auth_events:
auth_events[(ev.type, ev.state_key)] = ev
except AuthError:
pass

try:
self.auth.check(room_version, event, auth_events=auth_events)
except AuthError as e:
logger.warn("Failed auth resolution for %r because %s", event, e)
raise e
except Exception:
# FIXME:
logger.exception("Failed to query auth chain")

# 4. Look at rejects and their proofs.
# TODO.

yield self._update_context_for_auth_events(
event, context, auth_events, event_key,
)

@defer.inlineCallbacks
def _update_context_for_auth_events(self, event, context, auth_events,
Expand Down
2 changes: 1 addition & 1 deletion synapse/storage/events_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ def f(txn):

return res

return self.runInteraction("get_rejection_reasons", f)
return self.runInteraction("get_seen_events_with_rejections", f)

def _get_total_state_event_counts_txn(self, txn, room_id):
"""
Expand Down