Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
GatorPermissionsMap,
StoredGatorPermission,
PermissionTypesWithCustom,
RevocationParams,
} from './types';
import type {
ExtractAvailableAction,
Expand Down Expand Up @@ -688,6 +689,91 @@ describe('GatorPermissionsController', () => {
).toThrow('Failed to decode permission');
});
});

describe('submitRevocation', () => {
it('should successfully submit a revocation when gator permissions are enabled', async () => {
const mockHandleRequestHandler = jest.fn().mockResolvedValue(undefined);
const messenger = getMessenger(
getRootMessenger({
snapControllerHandleRequestActionHandler: mockHandleRequestHandler,
}),
);

const controller = new GatorPermissionsController({
messenger,
state: {
isGatorPermissionsEnabled: true,
gatorPermissionsProviderSnapId:
MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID,
},
});

const revocationParams: RevocationParams = {
delegationHash: '0x1234567890abcdef1234567890abcdef12345678',
};

await controller.submitRevocation(revocationParams);

expect(mockHandleRequestHandler).toHaveBeenCalledWith({
snapId: MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID,
origin: 'metamask',
handler: 'onRpcRequest',
request: {
jsonrpc: '2.0',
method: 'permissionsProvider_submitRevocation',
params: revocationParams,
},
});
});

it('should throw GatorPermissionsNotEnabledError when gator permissions are disabled', async () => {
const messenger = getMessenger();
const controller = new GatorPermissionsController({
messenger,
state: {
isGatorPermissionsEnabled: false,
},
});

const revocationParams: RevocationParams = {
delegationHash: '0x1234567890abcdef1234567890abcdef12345678',
};

await expect(
controller.submitRevocation(revocationParams),
).rejects.toThrow('Gator permissions are not enabled');
});

it('should throw GatorPermissionsProviderError when snap request fails', async () => {
const mockHandleRequestHandler = jest
.fn()
.mockRejectedValue(new Error('Snap request failed'));
const messenger = getMessenger(
getRootMessenger({
snapControllerHandleRequestActionHandler: mockHandleRequestHandler,
}),
);

const controller = new GatorPermissionsController({
messenger,
state: {
isGatorPermissionsEnabled: true,
gatorPermissionsProviderSnapId:
MOCK_GATOR_PERMISSIONS_PROVIDER_SNAP_ID,
},
});

const revocationParams: RevocationParams = {
delegationHash: '0x1234567890abcdef1234567890abcdef12345678',
};

await expect(
controller.submitRevocation(revocationParams),
).rejects.toThrow(
'Failed to handle snap request to gator permissions provider for method permissionsProvider_submitRevocation',
);
});
});
});

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
type PermissionTypesWithCustom,
type StoredGatorPermission,
type DelegationDetails,
type RevocationParams,
} from './types';
import {
deserializeGatorPermissionsMap,
Expand Down Expand Up @@ -179,6 +180,14 @@ export type GatorPermissionsControllerDecodePermissionFromPermissionContextForOr
handler: GatorPermissionsController['decodePermissionFromPermissionContextForOrigin'];
};

/**
* The action which can be used to submit a revocation.
*/
export type GatorPermissionsControllerSubmitRevocationAction = {
type: `${typeof controllerName}:submitRevocation`;
handler: GatorPermissionsController['submitRevocation'];
};

/**
* All actions that {@link GatorPermissionsController} registers, to be called
* externally.
Expand All @@ -188,7 +197,8 @@ export type GatorPermissionsControllerActions =
| GatorPermissionsControllerFetchAndUpdateGatorPermissionsAction
| GatorPermissionsControllerEnableGatorPermissionsAction
| GatorPermissionsControllerDisableGatorPermissionsAction
| GatorPermissionsControllerDecodePermissionFromPermissionContextForOriginAction;
| GatorPermissionsControllerDecodePermissionFromPermissionContextForOriginAction
| GatorPermissionsControllerSubmitRevocationAction;

/**
* All actions that {@link GatorPermissionsController} calls internally.
Expand Down Expand Up @@ -298,6 +308,11 @@ export default class GatorPermissionsController extends BaseController<
`${controllerName}:decodePermissionFromPermissionContextForOrigin`,
this.decodePermissionFromPermissionContextForOrigin.bind(this),
);

this.messagingSystem.registerActionHandler(
`${controllerName}:submitRevocation`,
this.submitRevocation.bind(this),
);
}

/**
Expand Down Expand Up @@ -598,4 +613,41 @@ export default class GatorPermissionsController extends BaseController<
});
}
}

/**
* Submits a revocation to the gator permissions provider snap.
*
* @param revocationParams - The revocation parameters containing the delegation hash.
* @returns A promise that resolves when the revocation is submitted successfully.
* @throws {GatorPermissionsNotEnabledError} If the gator permissions are not enabled.
* @throws {GatorPermissionsProviderError} If the snap request fails.
*/
public async submitRevocation(
revocationParams: RevocationParams,
): Promise<void> {
this.#assertGatorPermissionsEnabled();

try {
await this.messagingSystem.call('SnapController:handleRequest', {
snapId: this.state.gatorPermissionsProviderSnapId,
origin: 'metamask',
handler: HandlerType.OnRpcRequest,
request: {
jsonrpc: '2.0',
method:
GatorPermissionsSnapRpcMethod.PermissionProviderSubmitRevocation,
params: revocationParams,
},
});

controllerLog('Successfully submitted revocation', revocationParams);
} catch (error) {
controllerLog('Failed to submit revocation', error);
throw new GatorPermissionsProviderError({
method:
GatorPermissionsSnapRpcMethod.PermissionProviderSubmitRevocation,
cause: error as Error,
});
}
}
}
2 changes: 2 additions & 0 deletions packages/gator-permissions-controller/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type {
GatorPermissionsControllerFetchAndUpdateGatorPermissionsAction,
GatorPermissionsControllerEnableGatorPermissionsAction,
GatorPermissionsControllerDisableGatorPermissionsAction,
GatorPermissionsControllerSubmitRevocationAction,
GatorPermissionsControllerActions,
GatorPermissionsControllerEvents,
GatorPermissionsControllerStateChangeEvent,
Expand All @@ -31,6 +32,7 @@ export type {
GatorPermissionsMapByPermissionType,
GatorPermissionsListByPermissionTypeAndChainId,
DelegationDetails,
RevocationParams,
} from './types';

export type {
Expand Down
14 changes: 14 additions & 0 deletions packages/gator-permissions-controller/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export enum GatorPermissionsSnapRpcMethod {
* This method is used by the metamask to request a permissions provider to get granted permissions for all sites.
*/
PermissionProviderGetGrantedPermissions = 'permissionsProvider_getGrantedPermissions',
/**
* This method is used by the metamask to submit a revocation to the permissions provider.
*/
PermissionProviderSubmitRevocation = 'permissionsProvider_submitRevocation',
}

/**
Expand Down Expand Up @@ -220,3 +224,13 @@ export type DelegationDetails = Pick<
Delegation<Hex>,
'caveats' | 'delegator' | 'delegate' | 'authority'
>;

/**
* Represents the parameters for submitting a revocation.
*/
export type RevocationParams = {
/**
* The delegation hash as a hex string that identifies the permission to revoke.
*/
delegationHash: Hex;
};
Loading