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

Commit

Permalink
Simplifications and comments in do_auth (#5227)
Browse files Browse the repository at this point in the history
I was staring at this function trying to figure out wtf it was actually
doing. This is (hopefully) a non-functional refactor which makes it a bit
clearer.
  • Loading branch information
richvdh authored May 23, 2019
1 parent 1a94de6 commit 85d1e03
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 121 deletions.
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??
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

0 comments on commit 85d1e03

Please sign in to comment.