Skip to content

Commit

Permalink
fix: reject JoinIdentity authorizations correctly
Browse files Browse the repository at this point in the history
Adjusted how we handle third party fees

BREAKING CHANGE: `PolymeshTransaction.getFees` no longer returns fees as 0 when they are paid by a
third party. This is now reflected by a `paidByThirdParty` flag
  • Loading branch information
monitz87 committed Nov 11, 2020
1 parent aea1283 commit d225d38
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 138 deletions.
23 changes: 19 additions & 4 deletions src/api/entities/AuthorizationRequest.ts
@@ -1,7 +1,7 @@
import BigNumber from 'bignumber.js';

import { Entity, Identity } from '~/api/entities';
import { acceptJoinIdentityAuthorization, consumeAuthorizationRequests } from '~/api/procedures';
import { consumeAuthorizationRequests, consumeJoinIdentityAuthorization } from '~/api/procedures';
import { Context, TransactionQueue } from '~/base';
import { Authorization, AuthorizationType, Signer } from '~/types';

Expand Down Expand Up @@ -91,10 +91,13 @@ export class AuthorizationRequest extends Entity<UniqueIdentifiers> {
* Accept the authorization request. You must be the target of the request to be able to accept it
*/
public accept(): Promise<TransactionQueue> {
const { context } = this;
const {
context,
data: { type },
} = this;

if (this.data.type === AuthorizationType.JoinIdentity) {
return acceptJoinIdentityAuthorization.prepare({ authRequest: this }, context);
if (type === AuthorizationType.JoinIdentity) {
return consumeJoinIdentityAuthorization.prepare({ authRequest: this, accept: true }, context);
}

return consumeAuthorizationRequests.prepare({ authRequests: [this], accept: true }, context);
Expand All @@ -107,6 +110,18 @@ export class AuthorizationRequest extends Entity<UniqueIdentifiers> {
* - If you are the request target, this will reject the authorization
*/
public remove(): Promise<TransactionQueue> {
const {
context,
data: { type },
} = this;

if (type === AuthorizationType.JoinIdentity) {
return consumeJoinIdentityAuthorization.prepare(
{ authRequest: this, accept: false },
context
);
}

return consumeAuthorizationRequests.prepare(
{ authRequests: [this], accept: false },
this.context
Expand Down
38 changes: 34 additions & 4 deletions src/api/entities/__tests__/AuthorizationRequest.ts
Expand Up @@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js';
import sinon from 'sinon';

import { AuthorizationRequest, Entity, Identity } from '~/api/entities';
import { acceptJoinIdentityAuthorization, consumeAuthorizationRequests } from '~/api/procedures';
import { consumeAuthorizationRequests, consumeJoinIdentityAuthorization } from '~/api/procedures';
import { Context, TransactionQueue } from '~/base';
import { dsMockUtils } from '~/testUtils/mocks';
import { Authorization, AuthorizationType } from '~/types';
Expand Down Expand Up @@ -92,7 +92,7 @@ describe('AuthorizationRequest class', () => {
expect(queue).toBe(expectedQueue);
});

test('should prepare the acceptJoinIdentityAuthorization procedure with the correct arguments and context, and return the resulting transaction queue', async () => {
test('should prepare the consumeJoinIdentityAuthorization procedure with the correct arguments and context, and return the resulting transaction queue', async () => {
const authorizationRequest = new AuthorizationRequest(
{
authId: new BigNumber(1),
Expand All @@ -106,12 +106,13 @@ describe('AuthorizationRequest class', () => {

const args = {
authRequest: authorizationRequest,
accept: true,
};

const expectedQueue = ('someQueue' as unknown) as TransactionQueue<void>;

sinon
.stub(acceptJoinIdentityAuthorization, 'prepare')
.stub(consumeJoinIdentityAuthorization, 'prepare')
.withArgs({ ...args }, context)
.resolves(expectedQueue);

Expand All @@ -126,7 +127,7 @@ describe('AuthorizationRequest class', () => {
sinon.restore();
});

test('should prepare the procedure with the correct arguments and context, and return the resulting transaction queue', async () => {
test('should prepare the consumeAuthorizationRequest procedure with the correct arguments and context, and return the resulting transaction queue', async () => {
const authorizationRequest = new AuthorizationRequest(
{
authId: new BigNumber(1),
Expand Down Expand Up @@ -154,5 +155,34 @@ describe('AuthorizationRequest class', () => {

expect(queue).toBe(expectedQueue);
});

test('should prepare the consumeJoinIdentityAuthorization procedure with the correct arguments and context, and return the resulting transaction queue', async () => {
const authorizationRequest = new AuthorizationRequest(
{
authId: new BigNumber(1),
expiry: null,
target: new Identity({ did: 'someDid' }, context),
issuer: new Identity({ did: 'otherDid' }, context),
data: { type: AuthorizationType.JoinIdentity, value: [] },
},
context
);

const args = {
authRequest: authorizationRequest,
accept: false,
};

const expectedQueue = ('someQueue' as unknown) as TransactionQueue<void>;

sinon
.stub(consumeJoinIdentityAuthorization, 'prepare')
.withArgs({ ...args }, context)
.resolves(expectedQueue);

const queue = await authorizationRequest.remove();

expect(queue).toBe(expectedQueue);
});
});
});
Expand Up @@ -4,17 +4,17 @@ import sinon from 'sinon';

import { Account, AuthorizationRequest, Identity } from '~/api/entities';
import {
AcceptJoinIdentityAuthorizationParams,
ConsumeJoinIdentityAuthorizationParams,
isAuthorized,
prepareAcceptJoinIdentityAuthorization,
} from '~/api/procedures/acceptJoinIdentityAuthorization';
prepareConsumeJoinIdentityAuthorization,
} from '~/api/procedures/consumeJoinIdentityAuthorization';
import { Context } from '~/base';
import { dsMockUtils, entityMockUtils, procedureMockUtils } from '~/testUtils/mocks';
import { Mocked } from '~/testUtils/types';
import { Authorization, AuthorizationType } from '~/types';
import * as utilsModule from '~/utils';

describe('acceptJoinIdentityAuthorization procedure', () => {
describe('consumeJoinIdentityAuthorization procedure', () => {
let mockContext: Mocked<Context>;
let numberToU64Stub: sinon.SinonStub<[number | BigNumber, Context], u64>;
let authId: BigNumber;
Expand Down Expand Up @@ -52,15 +52,15 @@ describe('acceptJoinIdentityAuthorization procedure', () => {
});

test('should add a joinIdentityAsKey transaction to the queue if the target is an Account', async () => {
const proc = procedureMockUtils.getInstance<AcceptJoinIdentityAuthorizationParams, void>(
const proc = procedureMockUtils.getInstance<ConsumeJoinIdentityAuthorizationParams, void>(
mockContext
);

const transaction = dsMockUtils.createTxStub('identity', 'joinIdentityAsKey');

const target = new Account({ address: 'someAddress' }, mockContext);

await prepareAcceptJoinIdentityAuthorization.call(proc, {
await prepareConsumeJoinIdentityAuthorization.call(proc, {
authRequest: new AuthorizationRequest(
{
target,
Expand All @@ -71,21 +71,22 @@ describe('acceptJoinIdentityAuthorization procedure', () => {
},
mockContext
),
accept: true,
});

sinon.assert.calledWith(addTransactionStub, transaction, {}, rawAuthId);
sinon.assert.calledWith(addTransactionStub, transaction, { paidByThirdParty: true }, rawAuthId);
});

test('should add a joinIdentityAsIdentity transaction to the queue if the target is an Identity', async () => {
const proc = procedureMockUtils.getInstance<AcceptJoinIdentityAuthorizationParams, void>(
const proc = procedureMockUtils.getInstance<ConsumeJoinIdentityAuthorizationParams, void>(
mockContext
);

const transaction = dsMockUtils.createTxStub('identity', 'joinIdentityAsIdentity');

const target = new Identity({ did: 'someOtherDid' }, mockContext);

await prepareAcceptJoinIdentityAuthorization.call(proc, {
await prepareConsumeJoinIdentityAuthorization.call(proc, {
authRequest: new AuthorizationRequest(
{
target,
Expand All @@ -96,14 +97,53 @@ describe('acceptJoinIdentityAuthorization procedure', () => {
},
mockContext
),
accept: true,
});

sinon.assert.calledWith(addTransactionStub, transaction, {}, rawAuthId);
sinon.assert.calledWith(addTransactionStub, transaction, { paidByThirdParty: true }, rawAuthId);
});

test('should add a removeAuthorization transaction to the queue if accept is set to false', async () => {
const proc = procedureMockUtils.getInstance<ConsumeJoinIdentityAuthorizationParams, void>(
mockContext
);

const transaction = dsMockUtils.createTxStub('identity', 'removeAuthorization');

const target = new Identity({ did: 'someOtherDid' }, mockContext);

const rawSignatory = dsMockUtils.createMockSignatory({
Identity: dsMockUtils.createMockIdentityId(target.did),
});

sinon.stub(utilsModule, 'signerValueToSignatory').returns(rawSignatory);

await prepareConsumeJoinIdentityAuthorization.call(proc, {
authRequest: new AuthorizationRequest(
{
target,
issuer: entityMockUtils.getIdentityInstance(),
authId,
expiry: null,
data: { type: AuthorizationType.JoinIdentity, value: [] },
},
mockContext
),
accept: false,
});

sinon.assert.calledWith(
addTransactionStub,
transaction,
{ paidByThirdParty: true },
rawSignatory,
rawAuthId
);
});

describe('isAuthorized', () => {
test('should return whether the current Identity or Account is the target of the authorization request', async () => {
const proc = procedureMockUtils.getInstance<AcceptJoinIdentityAuthorizationParams, void>(
const proc = procedureMockUtils.getInstance<ConsumeJoinIdentityAuthorizationParams, void>(
mockContext
);
const { address } = mockContext.getCurrentAccount();
Expand All @@ -118,6 +158,7 @@ describe('acceptJoinIdentityAuthorization procedure', () => {
};
const args = {
authRequest: new AuthorizationRequest(constructorParams, mockContext),
accept: true,
};

const boundFunc = isAuthorized.bind(proc);
Expand Down
@@ -1,20 +1,21 @@
import { Account, AuthorizationRequest } from '~/api/entities';
import { Procedure } from '~/base';
import { numberToU64 } from '~/utils';
import { numberToU64, signerToSignerValue, signerValueToSignatory } from '~/utils';

/**
* @hidden
*/
export type AcceptJoinIdentityAuthorizationParams = {
export type ConsumeJoinIdentityAuthorizationParams = {
authRequest: AuthorizationRequest;
accept: boolean;
};

/**
* @hidden
*/
export async function prepareAcceptJoinIdentityAuthorization(
this: Procedure<AcceptJoinIdentityAuthorizationParams>,
args: AcceptJoinIdentityAuthorizationParams
export async function prepareConsumeJoinIdentityAuthorization(
this: Procedure<ConsumeJoinIdentityAuthorizationParams>,
args: ConsumeJoinIdentityAuthorizationParams
): Promise<void> {
const {
context: {
Expand All @@ -26,25 +27,39 @@ export async function prepareAcceptJoinIdentityAuthorization(
} = this;
const {
authRequest: { target, authId },
accept,
} = args;

let transaction;

const rawAuthId = numberToU64(authId, context);

if (!accept) {
this.addTransaction(
identity.removeAuthorization,
{ paidByThirdParty: true },
signerValueToSignatory(signerToSignerValue(target), context),
rawAuthId
);

return;
}

if (target instanceof Account) {
transaction = identity.joinIdentityAsKey;
} else {
transaction = identity.joinIdentityAsIdentity;
}

this.addTransaction(transaction, {}, numberToU64(authId, context));
this.addTransaction(transaction, { paidByThirdParty: true }, rawAuthId);
}

/**
* @hidden
*/
export async function isAuthorized(
this: Procedure<AcceptJoinIdentityAuthorizationParams>,
{ authRequest }: AcceptJoinIdentityAuthorizationParams
this: Procedure<ConsumeJoinIdentityAuthorizationParams>,
{ authRequest }: ConsumeJoinIdentityAuthorizationParams
): Promise<boolean> {
const { target } = authRequest;
const { context } = this;
Expand All @@ -65,7 +80,7 @@ export async function isAuthorized(
/**
* @hidden
*/
export const acceptJoinIdentityAuthorization = new Procedure(
prepareAcceptJoinIdentityAuthorization,
export const consumeJoinIdentityAuthorization = new Procedure(
prepareConsumeJoinIdentityAuthorization,
isAuthorized
);
6 changes: 3 additions & 3 deletions src/api/procedures/index.ts
@@ -1,9 +1,9 @@
// NOTE uncomment in Governance v2 upgrade

export {
acceptJoinIdentityAuthorization,
AcceptJoinIdentityAuthorizationParams,
} from './acceptJoinIdentityAuthorization';
consumeJoinIdentityAuthorization,
ConsumeJoinIdentityAuthorizationParams,
} from './consumeJoinIdentityAuthorization';
export { addInstruction, AddInstructionParams } from './addInstruction';
// export { cancelProposal } from './cancelProposal';
export { consumeAuthorizationRequests, ConsumeParams } from './consumeAuthorizationRequests';
Expand Down

0 comments on commit d225d38

Please sign in to comment.