Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion packages/gator-permissions-snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/snap-7715-permissions.git"
},
"source": {
"shasum": "e/MzuAeBepqg3OfGRH9GUTee7E1tvxWrwDIs9xKk+78=",
"shasum": "65G2xEE3Z7fstnXsOCp0jGjzwyXgCBEFBpsMieeLDhM=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
2 changes: 2 additions & 0 deletions packages/gator-permissions-snap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ const boundRpcHandlers: {
rpcHandler.grantPermission.bind(rpcHandler),
[RpcMethod.PermissionProviderGetPermissionOffers]:
rpcHandler.getPermissionOffers.bind(rpcHandler),
[RpcMethod.PermissionProviderGetGrantedPermissions]:
rpcHandler.getGrantedPermissions.bind(rpcHandler),
};

/**
Expand Down
1 change: 1 addition & 0 deletions packages/gator-permissions-snap/src/rpc/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const allowedPermissionsByOrigin: { [origin: string]: string[] } = {
RpcMethod.PermissionProviderGrantPermissions,
RpcMethod.PermissionProviderGetPermissionOffers,
],
metamask: [RpcMethod.PermissionProviderGetGrantedPermissions],
};

/**
Expand Down
20 changes: 20 additions & 0 deletions packages/gator-permissions-snap/src/rpc/rpcHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ export type RpcHandler = {
* @returns The permission offers.
*/
getPermissionOffers(): Promise<Json>;

/**
* Handles get granted permissions requests.
*
* @returns The granted permissions.
*/
getGrantedPermissions(): Promise<Json>;
};

/**
Expand Down Expand Up @@ -87,8 +94,21 @@ export function createRpcHandler(config: {
return DEFAULT_GATOR_PERMISSION_TO_OFFER;
};

/**
* Handles get granted permissions requests.
*
* @returns The granted permissions.
*/
const getGrantedPermissions = async (): Promise<Json> => {
logger.debug('getGrantedPermissions()');
const grantedPermission =
await profileSyncManager.getAllGrantedPermissions();
return grantedPermission as Json[];
};

return {
grantPermission,
getPermissionOffers,
getGrantedPermissions,
};
}
5 changes: 5 additions & 0 deletions packages/gator-permissions-snap/src/rpc/rpcMethod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ export enum RpcMethod {
* This method is used by the kernel to request a permissions provider to grant attenuated permissions to a site.
*/
PermissionProviderGrantPermissions = 'permissionsProvider_grantPermissions',

/**
* This method is used by Metamask clients to retrieve granted permissions for all sites.
*/
PermissionProviderGetGrantedPermissions = 'permissionsProvider_getGrantedPermissions',
}
13 changes: 13 additions & 0 deletions packages/gator-permissions-snap/test/end-to-end/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ describe('Kernel Snap', () => {
stack: expect.any(String),
});
});

it('throws an error if the origin is not metamask for getGrantedPermissions', async () => {
const response = await snapRequest({
method: 'permissionsProvider_getGrantedPermissions',
origin: 'npm:@metamask/not-metamask',
});

expect(response).toRespondWithError({
code: -32603,
message: `Origin 'npm:@metamask/not-metamask' is not allowed to call 'permissionsProvider_getGrantedPermissions'`,
stack: expect.any(String),
});
});
});
});
});
89 changes: 88 additions & 1 deletion packages/gator-permissions-snap/test/rpc/rpcHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ describe('RpcHandler', () => {
let handler: RpcHandler;
let mockHandler: jest.Mocked<PermissionHandlerType>;
let mockHandlerFactory: jest.Mocked<PermissionHandlerFactory>;
let mockProfileSyncManager: jest.Mocked<ProfileSyncManager>;

beforeEach(() => {
mockHandler = {
Expand All @@ -72,7 +73,7 @@ describe('RpcHandler', () => {
mockHandlerFactory = {
createPermissionHandler: jest.fn().mockReturnValue(mockHandler),
} as unknown as jest.Mocked<PermissionHandlerFactory>;
const mockProfileSyncManager = {
mockProfileSyncManager = {
revokeGrantedPermission: jest.fn(),
storeGrantedPermission: jest.fn(),
storeGrantedPermissionBatch: jest.fn(),
Expand Down Expand Up @@ -375,4 +376,90 @@ describe('RpcHandler', () => {
]);
});
});

describe('getGrantedPermissions', () => {
it('should return all granted permissions successfully', async () => {
const mockGrantedPermissions = [
{
permissionResponse: {
chainId: TEST_CHAIN_ID,
expiry: TEST_EXPIRY,
signer: {
type: 'account' as const,
data: { address: TEST_ADDRESS },
},
permission: {
type: 'test-permission',
data: { justification: 'Testing permission request' },
},
context: TEST_CONTEXT,
accountMeta: [],
signerMeta: {
delegationManager: TEST_ADDRESS,
},
},
siteOrigin: TEST_SITE_ORIGIN,
},
{
permissionResponse: {
chainId: '0x2' as const,
expiry: TEST_EXPIRY + 1000,
signer: {
type: 'account' as const,
data: {
address: '0x0987654321098765432109876543210987654321' as const,
},
},
permission: {
type: 'different-permission',
data: { justification: 'Another permission' },
},
context: '0xefgh' as const,
accountMeta: [],
signerMeta: {
delegationManager:
'0x0987654321098765432109876543210987654321' as const,
},
},
siteOrigin: 'https://another-example.com',
},
];

mockProfileSyncManager.getAllGrantedPermissions.mockResolvedValue(
mockGrantedPermissions,
);

const result = await handler.getGrantedPermissions();

expect(
mockProfileSyncManager.getAllGrantedPermissions,
).toHaveBeenCalledTimes(1);
expect(result).toStrictEqual(mockGrantedPermissions);
});

it('should return empty array when no permissions are granted', async () => {
mockProfileSyncManager.getAllGrantedPermissions.mockResolvedValue([]);

const result = await handler.getGrantedPermissions();

expect(
mockProfileSyncManager.getAllGrantedPermissions,
).toHaveBeenCalledTimes(1);
expect(result).toStrictEqual([]);
});

it('should handle errors from profile sync manager', async () => {
const errorMessage = 'Failed to retrieve granted permissions';
mockProfileSyncManager.getAllGrantedPermissions.mockRejectedValue(
new Error(errorMessage),
);

await expect(handler.getGrantedPermissions()).rejects.toThrow(
errorMessage,
);
expect(
mockProfileSyncManager.getAllGrantedPermissions,
).toHaveBeenCalledTimes(1);
});
});
});
Loading