var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import chaiAsPromised from 'chai-as-promised';
import sinon from 'sinon';
import chai, { expect } from 'chai';
import { DidKeyResolver } from '../../src/did/did-key-resolver.js';
import { GeneralJwsSigner } from '../../src/jose/jws/general/signer.js';
import { TestDataGenerator } from '../utils/test-data-generator.js';
import { TestStores } from '../test-stores.js';
import { TestStubGenerator } from '../utils/test-stub-generator.js';
import { DidResolver, Dwn, DwnErrorCode, Encoder, Jws, ProtocolsQuery } from '../../src/index.js';
import { DwnInterfaceName, DwnMethodName, Message } from '../../src/core/message.js';
import { getCurrentTimeInHighPrecision, sleep } from '../../src/utils/time.js';
chai.use(chaiAsPromised);
export function testProtocolsQueryHandler() {
    describe('ProtocolsQueryHandler.handle()', () => {
        if (process.env.TESTSOFF === 'true') {
            return;
        }
        let didResolver;
        let messageStore;
        let dataStore;
        let eventLog;
        let dwn;
        describe('functional tests', () => {
            // important to follow the `before` and `after` pattern to initialize and clean the stores in tests
            // so that different test suites can reuse the same backend store for testing
            before(() => __awaiter(this, void 0, void 0, function* () {
                didResolver = new DidResolver([new DidKeyResolver()]);
                const stores = TestStores.get();
                messageStore = stores.messageStore;
                dataStore = stores.dataStore;
                eventLog = stores.eventLog;
                dwn = yield Dwn.create({ didResolver, messageStore, dataStore, eventLog });
            }));
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                sinon.restore(); // wipe all previous stubs/spies/mocks/fakes
                // clean up before each test rather than after so that a test does not depend on other tests to do the clean up
                yield messageStore.clear();
                yield dataStore.clear();
                yield eventLog.clear();
            }));
            after(() => __awaiter(this, void 0, void 0, function* () {
                yield dwn.close();
            }));
            it('should return protocols matching the query', () => __awaiter(this, void 0, void 0, function* () {
                var _a, _b;
                const alice = yield TestDataGenerator.generatePersona();
                // setting up a stub method resolver
                TestStubGenerator.stubDidResolver(didResolver, [alice]);
                // insert three messages into DB, two with matching protocol
                const protocol1 = yield TestDataGenerator.generateProtocolsConfigure({ author: alice });
                const protocol2 = yield TestDataGenerator.generateProtocolsConfigure({ author: alice });
                const protocol3 = yield TestDataGenerator.generateProtocolsConfigure({ author: alice });
                yield dwn.processMessage(alice.did, protocol1.message, protocol1.dataStream);
                yield dwn.processMessage(alice.did, protocol2.message, protocol2.dataStream);
                yield dwn.processMessage(alice.did, protocol3.message, protocol3.dataStream);
                // testing singular conditional query
                const queryMessageData = yield TestDataGenerator.generateProtocolsQuery({
                    author: alice,
                    filter: { protocol: protocol1.message.descriptor.definition.protocol }
                });
                const reply = yield dwn.processMessage(alice.did, queryMessageData.message);
                expect(reply.status.code).to.equal(200);
                expect((_a = reply.entries) === null || _a === void 0 ? void 0 : _a.length).to.equal(1); // only 1 entry should match the query on protocol
                // testing fetch-all query without filter
                const queryMessageData2 = yield TestDataGenerator.generateProtocolsQuery({
                    author: alice
                });
                const reply2 = yield dwn.processMessage(alice.did, queryMessageData2.message);
                expect(reply2.status.code).to.equal(200);
                expect((_b = reply2.entries) === null || _b === void 0 ? void 0 : _b.length).to.equal(3); // expecting all 3 entries written above match the query
            }));
            it('should return published protocols matching the query if query is unauthenticated', () => __awaiter(this, void 0, void 0, function* () {
                // scenario: alice has 3 protocols installed: 1 private + 2 published
                var _c, _d;
                const alice = yield TestDataGenerator.generatePersona();
                // setting up a stub method resolver
                TestStubGenerator.stubDidResolver(didResolver, [alice]);
                // insert three messages into DB, two with matching protocol
                const protocol1 = yield TestDataGenerator.generateProtocolsConfigure({ author: alice, published: false });
                const protocol2 = yield TestDataGenerator.generateProtocolsConfigure({ author: alice, published: true });
                const protocol3 = yield TestDataGenerator.generateProtocolsConfigure({ author: alice, published: true });
                yield dwn.processMessage(alice.did, protocol1.message, protocol1.dataStream);
                yield dwn.processMessage(alice.did, protocol2.message, protocol2.dataStream);
                yield dwn.processMessage(alice.did, protocol3.message, protocol3.dataStream);
                // testing unauthenticated conditional query
                const conditionalQuery = yield ProtocolsQuery.create({
                    filter: { protocol: protocol2.message.descriptor.definition.protocol }
                });
                const conditionalQueryReply = yield dwn.processMessage(alice.did, conditionalQuery.message);
                expect(conditionalQueryReply.status.code).to.equal(200);
                expect((_c = conditionalQueryReply.entries) === null || _c === void 0 ? void 0 : _c.length).to.equal(1); // only 1 entry should match the query on protocol
                const protocolConfigured = conditionalQueryReply.entries[0];
                expect(protocolConfigured).to.deep.equal(protocol2.message);
                // testing fetch-all query without filter
                const fetchAllQuery = yield ProtocolsQuery.create({});
                const fetchAllQueryReply = yield dwn.processMessage(alice.did, fetchAllQuery.message);
                expect(fetchAllQueryReply.status.code).to.equal(200);
                expect((_d = fetchAllQueryReply.entries) === null || _d === void 0 ? void 0 : _d.length).to.equal(2);
                expect(fetchAllQueryReply.entries).to.deep.include(protocol2.message);
                expect(fetchAllQueryReply.entries).to.deep.include(protocol3.message);
            }));
            it('should return 400 if protocol is not normalized', () => __awaiter(this, void 0, void 0, function* () {
                const alice = yield DidKeyResolver.generate();
                // query for non-normalized protocol
                const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                    author: alice,
                    filter: { protocol: 'example.com/' },
                });
                // overwrite protocol because #create auto-normalizes protocol
                protocolsQuery.message.descriptor.filter.protocol = 'example.com/';
                // Re-create auth because we altered the descriptor after signing
                protocolsQuery.message.authorization = yield Message.signAsAuthorization(protocolsQuery.message.descriptor, Jws.createSignatureInput(alice));
                // Send records write message
                const reply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                expect(reply.status.code).to.equal(400);
                expect(reply.status.detail).to.contain(DwnErrorCode.UrlProtocolNotNormalized);
            }));
            it('should fail with 400 if `authorization` is referencing a different message (`descriptorCid`)', () => __awaiter(this, void 0, void 0, function* () {
                const { author, message, protocolsQuery } = yield TestDataGenerator.generateProtocolsQuery();
                const tenant = author.did;
                // replace `authorization` with incorrect `descriptorCid`, even though signature is still valid
                const incorrectDescriptorCid = yield TestDataGenerator.randomCborSha256Cid();
                const authorizationPayload = Object.assign({}, protocolsQuery.authorizationPayload);
                authorizationPayload.descriptorCid = incorrectDescriptorCid;
                const authorizationPayloadBytes = Encoder.objectToBytes(authorizationPayload);
                const signatureInput = Jws.createSignatureInput(author);
                const signer = yield GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput]);
                message.authorization = signer.getJws();
                const reply = yield dwn.processMessage(tenant, message);
                expect(reply.status.code).to.equal(400);
                expect(reply.status.detail).to.contain(`${incorrectDescriptorCid} does not match expected CID`);
            }));
            it('should return 401 if auth fails', () => __awaiter(this, void 0, void 0, function* () {
                const alice = yield DidKeyResolver.generate();
                alice.keyId = 'wrongValue'; // to fail authentication
                const { message } = yield TestDataGenerator.generateProtocolsQuery({ author: alice });
                const reply = yield dwn.processMessage(alice.did, message);
                expect(reply.status.code).to.equal(401);
                expect(reply.status.detail).to.contain('not a valid DID');
            }));
            it('rejects authenticated non-tenant non-granted ProtocolsConfigures with 401', () => __awaiter(this, void 0, void 0, function* () {
                // Bob tries to ProtocolsConfigure to Alice's DWN without a PermissionsGrant
                const alice = yield DidKeyResolver.generate();
                const bob = yield DidKeyResolver.generate();
                const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                    author: bob,
                });
                const protocolsQueryReply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                expect(protocolsQueryReply.status.code).to.equal(401);
                expect(protocolsQueryReply.status.detail).to.contain(DwnErrorCode.ProtocolsQueryUnauthorized);
            }));
            describe('Grant authorization', () => {
                it('allows an external party to ProtocolsConfigure if they have an active grant', () => __awaiter(this, void 0, void 0, function* () {
                    // scenario: Alice grants Bob the access to ProtocolsConfigure on her DWN, then Bob does a ProtocolsConfigure
                    const alice = yield DidKeyResolver.generate();
                    const bob = yield DidKeyResolver.generate();
                    // Alice gives Bob a PermissionsGrant with scope ProtocolsConfigure
                    const permissionsGrant = yield TestDataGenerator.generatePermissionsGrant({
                        author: alice,
                        grantedBy: alice.did,
                        grantedFor: alice.did,
                        grantedTo: bob.did,
                        scope: {
                            interface: DwnInterfaceName.Protocols,
                            method: DwnMethodName.Query,
                        }
                    });
                    const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
                    expect(permissionsGrantReply.status.code).to.equal(202);
                    // Bob does ProtocolsConfigure on Alice's DWN
                    const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                        author: bob,
                        permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                    });
                    const protocolsQueryReply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                    expect(protocolsQueryReply.status.code).to.equal(200);
                }));
                it('rejects with 401 an external party attempt to ProtocolsConfigure if they present an expired grant', () => __awaiter(this, void 0, void 0, function* () {
                    // scenario: Alice grants Bob access to ProtocolsConfigure, but when Bob invokes the grant it has expired
                    const alice = yield DidKeyResolver.generate();
                    const bob = yield DidKeyResolver.generate();
                    // Alice gives Bob a PermissionsGrant with scope ProtocolsConfigure and an expiry time
                    const dateGranted = getCurrentTimeInHighPrecision();
                    const dateExpires = getCurrentTimeInHighPrecision();
                    const permissionsGrant = yield TestDataGenerator.generatePermissionsGrant({
                        author: alice,
                        messageTimestamp: dateGranted,
                        dateExpires,
                        grantedBy: alice.did,
                        grantedFor: alice.did,
                        grantedTo: bob.did,
                        scope: {
                            interface: DwnInterfaceName.Protocols,
                            method: DwnMethodName.Query,
                        }
                    });
                    const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
                    expect(permissionsGrantReply.status.code).to.equal(202);
                    // Bob does ProtocolsConfigure after the grant has expired
                    const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                        author: bob,
                        permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                    });
                    const protocolsQueryReply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                    expect(protocolsQueryReply.status.code).to.equal(401);
                    expect(protocolsQueryReply.status.detail).to.contain(DwnErrorCode.GrantAuthorizationGrantExpired);
                }));
                it('rejects with 401 an external partys attempt to ProtocolsConfigure if the grant is not yet active', () => __awaiter(this, void 0, void 0, function* () {
                    // scenario: Alice grants Bob access to ProtocolsConfigure, but Bob's message has a timestamp just before the grant is active
                    const alice = yield DidKeyResolver.generate();
                    const bob = yield DidKeyResolver.generate();
                    // Set up timestamps
                    const protocolsQueryTimestamp = getCurrentTimeInHighPrecision();
                    yield sleep(1);
                    const dateGranted = getCurrentTimeInHighPrecision();
                    // Alice gives Bob a PermissionsGrant with scope ProtocolsConfigure
                    const permissionsGrant = yield TestDataGenerator.generatePermissionsGrant({
                        author: alice,
                        messageTimestamp: dateGranted,
                        grantedBy: alice.did,
                        grantedFor: alice.did,
                        grantedTo: bob.did,
                        scope: {
                            interface: DwnInterfaceName.Protocols,
                            method: DwnMethodName.Query,
                        }
                    });
                    const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
                    expect(permissionsGrantReply.status.code).to.equal(202);
                    // Bob does ProtocolsConfigure but his message has timestamp before the grant is active
                    const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                        author: bob,
                        messageTimestamp: protocolsQueryTimestamp,
                        permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                    });
                    const protocolsQueryReply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                    expect(protocolsQueryReply.status.code).to.equal(401);
                    expect(protocolsQueryReply.status.detail).to.contain(DwnErrorCode.GrantAuthorizationGrantNotYetActive);
                }));
                it('rejects with 401 an external partys attempt to ProtocolsConfigure if the grant has been revoked', () => __awaiter(this, void 0, void 0, function* () {
                    // Alice grants and revokes Bob access to ProtocolsConfigure. Bob tries to invoke the revoked grant
                    const alice = yield DidKeyResolver.generate();
                    const bob = yield DidKeyResolver.generate();
                    // Alice gives Bob a PermissionsGrant with scope ProtocolsConfigure
                    const permissionsGrant = yield TestDataGenerator.generatePermissionsGrant({
                        author: alice,
                        grantedBy: alice.did,
                        grantedFor: alice.did,
                        grantedTo: bob.did,
                        scope: {
                            interface: DwnInterfaceName.Protocols,
                            method: DwnMethodName.Query,
                        }
                    });
                    const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
                    expect(permissionsGrantReply.status.code).to.equal(202);
                    const permissionsGrantId = yield Message.getCid(permissionsGrant.message);
                    // Alice revokes Bob's grant
                    const permissionsRevoke = yield TestDataGenerator.generatePermissionsRevoke({
                        author: alice,
                        permissionsGrantId,
                    });
                    const permissionsRevokeReply = yield dwn.processMessage(alice.did, permissionsRevoke.message);
                    expect(permissionsRevokeReply.status.code).to.equal(202);
                    // Bob does ProtocolsConfigure with the revoked grant
                    const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                        author: bob,
                        permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                    });
                    const protocolsQueryReply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                    expect(protocolsQueryReply.status.code).to.equal(401);
                    expect(protocolsQueryReply.status.detail).to.contain(DwnErrorCode.GrantAuthorizationGrantRevoked);
                }));
                it('rejects with 401 an external party attempts to ProtocolsConfigure if grant has different DWN interface scope', () => __awaiter(this, void 0, void 0, function* () {
                    // scenario: Alice grants Bob access to RecordsRead, then Bob tries to invoke the grant with ProtocolsConfigure
                    const alice = yield DidKeyResolver.generate();
                    const bob = yield DidKeyResolver.generate();
                    // Alice gives Bob a PermissionsGrant with scope RecordsRead
                    const permissionsGrant = yield TestDataGenerator.generatePermissionsGrant({
                        author: alice,
                        grantedBy: alice.did,
                        grantedFor: alice.did,
                        grantedTo: bob.did,
                        scope: {
                            interface: DwnInterfaceName.Records,
                            method: DwnMethodName.Read,
                        }
                    });
                    const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
                    expect(permissionsGrantReply.status.code).to.equal(202);
                    // Bob tries to ProtocolsConfigure
                    const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                        author: bob,
                        permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                    });
                    const protocolsQueryReply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                    expect(protocolsQueryReply.status.code).to.equal(401);
                    expect(protocolsQueryReply.status.detail).to.contain(DwnErrorCode.GrantAuthorizationInterfaceMismatch);
                }));
                it('rejects with 401 an external party attempts to ProtocolsConfigure if grant has different DWN method scope', () => __awaiter(this, void 0, void 0, function* () {
                    // scenario: Alice grants Bob access to ProtocolsQuery, then Bob tries to invoke the grant with ProtocolsConfigure
                    const alice = yield DidKeyResolver.generate();
                    const bob = yield DidKeyResolver.generate();
                    // Alice gives Bob a PermissionsGrant with scope RecordsRead
                    const permissionsGrant = yield TestDataGenerator.generatePermissionsGrant({
                        author: alice,
                        grantedBy: alice.did,
                        grantedFor: alice.did,
                        grantedTo: bob.did,
                        scope: {
                            interface: DwnInterfaceName.Protocols,
                            method: DwnMethodName.Configure,
                        }
                    });
                    const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
                    expect(permissionsGrantReply.status.code).to.equal(202);
                    // Bob tries to ProtocolsConfigure
                    const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                        author: bob,
                        permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                    });
                    const protocolsQueryReply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                    expect(protocolsQueryReply.status.code).to.equal(401);
                    expect(protocolsQueryReply.status.detail).to.contain(DwnErrorCode.GrantAuthorizationMethodMismatch);
                }));
                it('rejects with 401 if the PermissionsGrant cannot be found', () => __awaiter(this, void 0, void 0, function* () {
                    // scenario: Bob uses a permissionsGrantId to ProtocolsConfigure, but no PermissionsGrant can be found.
                    const alice = yield DidKeyResolver.generate();
                    const bob = yield DidKeyResolver.generate();
                    // Bob tries to ProtocolsConfigure
                    const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                        author: bob,
                        permissionsGrantId: yield TestDataGenerator.randomCborSha256Cid(),
                    });
                    const protocolsQueryReply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                    expect(protocolsQueryReply.status.code).to.equal(401);
                    expect(protocolsQueryReply.status.detail).to.contain(DwnErrorCode.GrantAuthorizationGrantMissing);
                }));
                it('rejects with 401 if the PermissionsGrant has not been grantedTo the author', () => __awaiter(this, void 0, void 0, function* () {
                    // Alice gives a PermissionsGrant to Bob, then Carol tries to invoke it to ProtocolsConfigure on Alice's DWN
                    const alice = yield DidKeyResolver.generate();
                    const bob = yield DidKeyResolver.generate();
                    const carol = yield DidKeyResolver.generate();
                    // Alice gives Bob a PermissionsGrant with scope ProtocolsConfigure
                    const permissionsGrant = yield TestDataGenerator.generatePermissionsGrant({
                        author: alice,
                        grantedBy: alice.did,
                        grantedFor: alice.did,
                        grantedTo: bob.did,
                        scope: {
                            interface: DwnInterfaceName.Protocols,
                            method: DwnMethodName.Configure,
                        }
                    });
                    const permissionsGrantReply = yield dwn.processMessage(alice.did, permissionsGrant.message);
                    expect(permissionsGrantReply.status.code).to.equal(202);
                    // Carol tries to use Bob's PermissionsGrant to gain access to Alice's DWN
                    const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                        author: carol,
                        permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                    });
                    const protocolsQueryReply = yield dwn.processMessage(alice.did, protocolsQuery.message);
                    expect(protocolsQueryReply.status.code).to.equal(401);
                    expect(protocolsQueryReply.status.detail).to.contain(DwnErrorCode.GrantAuthorizationNotGrantedToAuthor);
                }));
                it('rejects with 401 if the PermissionsGrant has not been grantedFor the tenant', () => __awaiter(this, void 0, void 0, function* () {
                    // Alice gives a PermissionsGrant to Carol, which Bob stores on his DWN.
                    // Then Carol tries to invoke it to ProtocolsConfigure on Bob's DWN.
                    const alice = yield DidKeyResolver.generate();
                    const bob = yield DidKeyResolver.generate();
                    const carol = yield DidKeyResolver.generate();
                    // Alice gives Bob a PermissionsGrant with scope ProtocolsConfigure
                    const permissionsGrant = yield TestDataGenerator.generatePermissionsGrant({
                        author: alice,
                        grantedBy: alice.did,
                        grantedFor: alice.did,
                        grantedTo: carol.did,
                        scope: {
                            interface: DwnInterfaceName.Protocols,
                            method: DwnMethodName.Configure,
                        }
                    });
                    const permissionsGrantReply = yield dwn.processMessage(bob.did, permissionsGrant.message);
                    expect(permissionsGrantReply.status.code).to.equal(202);
                    // Carol tries to use Bob's PermissionsGrant to gain access to Bob's DWN
                    const protocolsQuery = yield TestDataGenerator.generateProtocolsQuery({
                        author: carol,
                        permissionsGrantId: yield Message.getCid(permissionsGrant.message),
                    });
                    const protocolsQueryReply = yield dwn.processMessage(bob.did, protocolsQuery.message);
                    expect(protocolsQueryReply.status.code).to.equal(401);
                    expect(protocolsQueryReply.status.detail).to.contain(DwnErrorCode.GrantAuthorizationNotGrantedForTenant);
                }));
            });
        });
    });
}
//# sourceMappingURL=protocols-query.spec.js.map