Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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": "fMzK/tguXx0jZMJ3Vu9z1OcEFZD3LDYPS8mL7LWSSc8=",
"shasum": "8PGSI83NMAkED4BCqQY6WUB6rSDGmHLx2PavNnyQm0s=",
"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