dwn-sdk-js icon indicating copy to clipboard operation
dwn-sdk-js copied to clipboard

Add `query` to list of supported actions for authors/recipients

Open thehenrytsai opened this issue 1 year ago • 12 comments

Currently only write is supported.

thehenrytsai avatar Mar 29 '23 23:03 thehenrytsai

Should add read to this as well.

csuwildcat avatar Mar 29 '23 23:03 csuwildcat

Needs to be complete by EOD May 14th at the latest

frankhinek avatar May 01 '23 18:05 frankhinek

I will note that 'completed' here = in a released version of the DWN SDK and Web5 JS, so probably needs to land as a PR well before that.

csuwildcat avatar May 01 '23 18:05 csuwildcat

I've thought about protocol query a bit more. I think I've figured out what the "difficult" part is and I have an idea of how to approach it.

Difficult Part

The current ProtocolAuthorization#authorize code is meant to look at the protocol definition for a single rule set based on the given actor and protocol path. For query, we potentially need to look down multiple protocolPaths to get multiple rule sets. So, calling authorize once at the top of RecordsQuery#handler won't be sufficient. We need to do some authorization for each record returned from the message store query.

Approach

If protocol appears in the RecordsQuery filters: First, do a messageStore#query using all the filters given in the RecordsQuery descriptor. This will give us a list of RecordsWrites. Then, we iterate over each RecordsWrite to get the associated rule set for recordsWrite.descriptor.protocolPath and the actor of the RecordsQuery.

Open Questions

  • What do we return when the RecordsQuery filter does not specify protocol? Do we omit all protocol records? Or include protocol records, checking each protocol definition to see if the record is auth'd for query? If we include all that are allowed, that involves looking through potentially all protocol definitions on the DWN. Not sure if we want that.
    • @csuwildcat This is probably for you to answer.

diehuxx avatar May 03 '23 17:05 diehuxx

I would require protocol to be set if the protocolPath is set, and not search through protocols at all if protocol isn't set.

csuwildcat avatar May 03 '23 17:05 csuwildcat

Agree that enforcing protocol as a filter will help scope the search of a protocol-authorized query.

Then, we iterate over each RecordsWrite to get the associated rule set for recordsWrite.descriptor.protocolPath and the actor of the RecordsQuery.

This seems super heavy on processing. Is it possible to perhaps "flip" the logic? As in, go through the entire protocol definition hierarchy to find the authorized protocol paths matching the query, then perform a scoped query for each matched protocol path?

Also maybe I misinterpreted it, I vaguely recall @csuwildcat told us a couple days ago that he was okay for a protocol-authorized query to return records of a specific protocol path only? If that's true, then we can just make protocolPath also a required property for protocol-authorized query and it will make our lives much easier.

thehenrytsai avatar May 04 '23 16:05 thehenrytsai

I have no issue making protocolPath required for protocol-related queries

csuwildcat avatar May 04 '23 17:05 csuwildcat

As in, go through the entire protocol definition hierarchy to find the authorized protocol paths matching the query, then perform a scoped query for each matched protocol path?

I like this. But you're right that requiring protocolPath would make it a moot point.

I have no issue making protocolPath required for protocol-related queries

Nice. This makes things way easier.

diehuxx avatar May 04 '23 20:05 diehuxx

@csuwildcat and @diehuxx, should/can we add an extra requirement that contextId needs to exist in the query also?

thehenrytsai avatar May 10 '23 01:05 thehenrytsai

Adding a note for my own benefit:

Someone at DWN office hours on discord flagged that they have a use case for query and would like this implemented. If someone is the recipient of a doctor record, they should be able to query for patientFiles records, which the doctor is obviously not the recipient of.

I'll update this week or next with a timeline for implementing query.

diehuxx avatar Aug 01 '23 18:08 diehuxx

I understand the problem here, and at first blush it seems like on write of a record we should index the DIDs that would be able to access it, if we didn't want to do that dynamically.

csuwildcat avatar Sep 21 '23 20:09 csuwildcat

Summary of a discussion just now:

There a few cases to break protocol queries into.

Summary

Add a new index protocolLineage to RecordsWrites that contains all the recordIds of the ancestor tree for a protocol message, with the root recordId at the beginning.

For $globalRole-auth'd queries, we require protocolPath to appear in the RecordsQuery filter. For $contextRole and who: 'author' | 'recipient', we require both protocolPath and contextId to appear in the query.

$globalRole

protocolPath are required in the RecordsQuery filter. if those are not present, return empty list.

  1. Get the protocol rule set for the protocolPath being queried.
  2. Check if there is a role $action rule that matches the $globalRole path.
  3. Check if the queryer has a $globalRole.
  4. If yes to both checks, query the messageStore and return all matching messages. Otherwise, return empty list.

$contextRole

protocolPath and contextId are required in the RecordsQuery filter. if those are not present, return empty list.

  1. Get the protocol rule set for the protocol path being queried.
  2. Check if there is a role $action rule that matches the $contextRole path.
  3. Check if there is a $contextRole in that contextId
  4. If yes to both checks, query the messageStore and return all matching messages. Otherwise, return empty list.

who: 'anyone' | 'recipient' | 'anyone'

This is the most complex and may require further discussion. For now, we aim to implement role-auth'd queries first. protocolPath is required in the RecordsQuery filter for who: 'anyone' to work. Both protocolPath and contextId are required for who: 'author' | 'recipient' to work.

  1. Get the protocol rule set for the protocol path being queried.
  2. If there is a who: 'anyone' rule, query the message store and return all matches.
  3. If contextId is not present, return empty list. Else, proceed to the next step.
  4. For each who: 'author' | 'recipient' rule a. Query message store, filtering by protocolPath: protocolActionRule.of and either author or recipient. Call this matchingAncestorRecords. b. Query message store, filtering for messages with matchingAncestorRecords.map((r) => r.recordId) in their ancestry list. Return all matching messages.

diehuxx avatar Sep 21 '23 21:09 diehuxx