Skip to content

Commit

Permalink
A non-tenant author of a Record should be able to RecordsRead the…
Browse files Browse the repository at this point in the history
…ir record. (#812)

Currently a `RecordsQuery` will return any records which you are the `author` of or `recipient` of, regardless of the protocol rules.

However `RecordsRead` would only return a record which you are the `recipient` of, and fail protocol rules for messages that you are an `author` of.

This PR more closely aligns the two by allowing a non-tenant to Read any record which they have authored regardless of the protocol rules.
  • Loading branch information
LiranCohen authored Oct 4, 2024
1 parent 7b02581 commit 31b0907
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 3 deletions.
6 changes: 4 additions & 2 deletions src/handlers/records-read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,10 @@ export class RecordsReadHandler implements MethodHandler {
} else if (descriptor.published === true) {
// authentication is not required for published data
return;
} else if (recordsRead.author !== undefined && recordsRead.author === descriptor.recipient) {
// The recipient of a message may always read it
} else if (recordsRead.author !== undefined &&
(recordsRead.author === descriptor.recipient || recordsRead.author === matchedRecordsWrite.author)
) {
// The recipient or author of a message may always read it
return;
} else if (recordsRead.author !== undefined && recordsRead.signaturePayload!.permissionGrantId !== undefined) {
const permissionGrant = await PermissionsProtocol.fetchGrant(tenant, messageStore, recordsRead.signaturePayload!.permissionGrantId);
Expand Down
68 changes: 67 additions & 1 deletion tests/handlers/records-read.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function testRecordsReadHandler(): void {
expect(ArrayUtility.byteArraysEqual(dataFetched, dataBytes!)).to.be.true;
});

it('should not allow non-tenant to RecordsRead their a record data', async () => {
it('should not allow non-tenant to RecordsRead a record', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();

// insert data
Expand Down Expand Up @@ -205,6 +205,72 @@ export function testRecordsReadHandler(): void {
expect(ArrayUtility.byteArraysEqual(dataFetched, dataBytes!)).to.be.true;
});

it('should allow a non-tenant to read RecordsRead data they have authored', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const bob = await TestDataGenerator.generateDidKeyPersona();
const carol = await TestDataGenerator.generateDidKeyPersona();

// Alice installs a protocol that allows anyone to write foo record
const protocolDefinition:ProtocolDefinition = {
published : true,
protocol : 'https://example.com/foo',
types : {
foo: {}
},
structure: {
foo: {
$actions: [{
who : 'anyone',
can : ['create']
}]
}
}
};

const configureProtocol = await TestDataGenerator.generateProtocolsConfigure({
author : alice,
protocolDefinition : protocolDefinition,
});
const configureProtocolReply = await dwn.processMessage(alice.did, configureProtocol.message);
expect(configureProtocolReply.status.code).to.equal(202);

// Bob writes a foo record to Alice's DWN
const { message, dataStream, dataBytes } = await TestDataGenerator.generateRecordsWrite({
author : bob,
protocol : protocolDefinition.protocol,
protocolPath : 'foo',
});
const writeReply = await dwn.processMessage(alice.did, message, { dataStream });
expect(writeReply.status.code).to.equal(202);

// Bob reads the record he sent to Alice from Alice's DWN
const recordsRead = await RecordsRead.create({
filter: {
recordId: message.recordId,
},
signer: Jws.createSigner(bob)
});

const readReply = await dwn.processMessage(alice.did, recordsRead.message);
expect(readReply.status.code).to.equal(200);
expect(readReply.record).to.exist;
expect(readReply.record?.descriptor).to.exist;

const dataFetched = await DataStream.toBytes(readReply.record!.data!);
expect(ArrayUtility.byteArraysEqual(dataFetched, dataBytes!)).to.be.true;

// carol attempts to read Bob's record
const carolRecordsRead = await RecordsRead.create({
filter: {
recordId: message.recordId,
},
signer: Jws.createSigner(carol)
});

const carolReadReply = await dwn.processMessage(alice.did, carolRecordsRead.message);
expect(carolReadReply.status.code).to.equal(401);
});

it('should include `initialWrite` property if RecordsWrite is not initial write', async () => {
const alice = await TestDataGenerator.generateDidKeyPersona();
const write = await TestDataGenerator.generateRecordsWrite({ author: alice, published: false });
Expand Down

0 comments on commit 31b0907

Please sign in to comment.