Skip to content

Commit

Permalink
feat(permissions): add key permission checks to procedures
Browse files Browse the repository at this point in the history
Added `CurrentAccount.hasPermissions` query
  • Loading branch information
monitz87 committed Dec 13, 2020
1 parent 4f0fa4f commit eefb544
Show file tree
Hide file tree
Showing 66 changed files with 1,584 additions and 574 deletions.
56 changes: 54 additions & 2 deletions src/api/entities/CurrentAccount.ts
@@ -1,6 +1,8 @@
import { difference, differenceBy, differenceWith, isEqual } from 'lodash';

import { Account, CurrentIdentity } from '~/internal';
import { Permissions } from '~/types';
import { signerToString } from '~/utils/conversion';
import { portfolioToPortfolioId, signerToString } from '~/utils/conversion';

/**
* Represents the current account that is bound to the SDK instance
Expand All @@ -16,7 +18,7 @@ export class CurrentAccount extends Account {
}

/**
* Retrieve the Permissions this Account has as a Signing Key for its corresponding Identity
* Retrieve the Permissions this Signer has as a Signing Key for its corresponding Identity
*/
public async getPermissions(): Promise<Permissions> {
const { context, address } = this;
Expand All @@ -41,4 +43,54 @@ export class CurrentAccount extends Account {

return key.permissions;
}

/**
* Check if this Account possesses certain Permissions for its corresponding Identity
*/
public async hasPermissions(permissions: Permissions): Promise<boolean> {
const { tokens, transactions, portfolios } = permissions;

const {
tokens: currentTokens,
transactions: currentTransactions,
portfolios: currentPortfolios,
} = await this.getPermissions();

let hasTokens;
if (currentTokens === null) {
hasTokens = true;
} else if (tokens === null) {
hasTokens = false;
} else {
hasTokens = tokens.length === 0 || !differenceBy(tokens, currentTokens, 'ticker').length;
}

let hasTransactions;
if (currentTransactions === null) {
hasTransactions = true;
} else if (transactions === null) {
hasTransactions = false;
} else {
hasTransactions =
transactions.length === 0 || !difference(transactions, currentTransactions).length;
}

let hasPortfolios;
if (currentPortfolios === null) {
hasPortfolios = true;
} else if (portfolios === null) {
hasPortfolios = false;
} else {
hasPortfolios =
portfolios.length === 0 ||
!differenceWith(portfolios, currentPortfolios, (a, b) => {
const aId = portfolioToPortfolioId(a);
const bId = portfolioToPortfolioId(b);

return isEqual(aId, bId);
}).length;
}

return hasTokens && hasTransactions && hasPortfolios;
}
}
7 changes: 4 additions & 3 deletions src/api/entities/CurrentIdentity.ts
Expand Up @@ -3,6 +3,7 @@ import P from 'bluebird';
import { chunk, flatten, uniqBy } from 'lodash';
import { Instruction as MeshInstruction } from 'polymesh-types/types';

import { assertPortfolioExists } from '~/api/procedures/utils';
import {
createVenue,
CreateVenueParams,
Expand Down Expand Up @@ -101,9 +102,9 @@ export class CurrentIdentity extends Identity {

const allPortfolios = [...ownedCustodiedPortfolios, ...custodiedPortfolios];

const portfolioIds = await P.map(allPortfolios, portfolio =>
portfolioLikeToPortfolioId(portfolio, context)
);
const portfolioIds = allPortfolios.map(portfolioLikeToPortfolioId);

await P.map(portfolioIds, portfolioId => assertPortfolioExists(portfolioId, context));

const portfolioIdChunks = chunk(portfolioIds, MAX_CONCURRENT_REQUESTS);

Expand Down
10 changes: 7 additions & 3 deletions src/api/entities/SecurityToken/Settlements.ts
@@ -1,6 +1,7 @@
import BigNumber from 'bignumber.js';
import { CanTransferResult } from 'polymesh-types/types';

import { assertPortfolioExists } from '~/api/procedures/utils';
import { Namespace, SecurityToken } from '~/internal';
import { PortfolioLike, TransferStatus } from '~/types';
import { DUMMY_ACCOUNT_ID } from '~/utils/constants';
Expand Down Expand Up @@ -59,9 +60,12 @@ export class Settlements extends Namespace<SecurityToken> {
*/
const senderAddress = context.currentPair?.address || DUMMY_ACCOUNT_ID;

const [fromPortfolio, toPortfolio] = await Promise.all([
portfolioLikeToPortfolioId(from, context),
portfolioLikeToPortfolioId(to, context),
const fromPortfolio = portfolioLikeToPortfolioId(from);
const toPortfolio = portfolioLikeToPortfolioId(to);

await Promise.all([
assertPortfolioExists(fromPortfolio, context),
assertPortfolioExists(toPortfolio, context),
]);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
13 changes: 4 additions & 9 deletions src/api/entities/SecurityToken/__tests__/Settlements.ts
Expand Up @@ -22,10 +22,7 @@ describe('Settlements class', () => {
let stringToTickerStub: SinonStub<[string, Context], Ticker>;
let numberToBalanceStub: sinon.SinonStub;
let portfolioIdToMeshPortfolioIdStub: sinon.SinonStub<[PortfolioId, Context], MeshPortfolioId>;
let portfolioLikeToPortfolioIdStub: sinon.SinonStub<
[PortfolioLike, Context],
Promise<PortfolioId>
>;
let portfolioLikeToPortfolioIdStub: sinon.SinonStub<[PortfolioLike], PortfolioId>;
let rawAccountId: AccountId;
let rawTicker: Ticker;
let rawAmount: Balance;
Expand Down Expand Up @@ -95,8 +92,8 @@ describe('Settlements class', () => {
});

beforeEach(() => {
portfolioLikeToPortfolioIdStub.withArgs(fromDid, mockContext).resolves(fromPortfolioId);
portfolioLikeToPortfolioIdStub.withArgs(toDid, mockContext).resolves(toPortfolioId);
portfolioLikeToPortfolioIdStub.withArgs(fromDid).returns(fromPortfolioId);
portfolioLikeToPortfolioIdStub.withArgs(toDid).returns(toPortfolioId);
portfolioIdToMeshPortfolioIdStub.withArgs(toPortfolioId, mockContext).returns(rawToPortfolio);
});

Expand All @@ -107,9 +104,7 @@ describe('Settlements class', () => {
const rawDummyAccountId = dsMockUtils.createMockAccountId(DUMMY_ACCOUNT_ID);
const currentDefaultPortfolioId = { did: currentDid };

portfolioLikeToPortfolioIdStub
.withArgs(currentIdentity, mockContext)
.resolves(currentDefaultPortfolioId);
portfolioLikeToPortfolioIdStub.withArgs(currentIdentity).returns(currentDefaultPortfolioId);
portfolioIdToMeshPortfolioIdStub
.withArgs(currentDefaultPortfolioId, mockContext)
.returns(rawFromPortfolio);
Expand Down
55 changes: 52 additions & 3 deletions src/api/entities/__tests__/CurrentAccount.ts
Expand Up @@ -2,7 +2,7 @@ import sinon from 'sinon';

import { Account, Context, CurrentAccount, CurrentIdentity, Identity } from '~/internal';
import { dsMockUtils, entityMockUtils } from '~/testUtils/mocks';
import { TxTags } from '~/types';
import { Permissions, TxTags } from '~/types';
import * as utilsConversionModule from '~/utils/conversion';

describe('CurrentAccount class', () => {
Expand Down Expand Up @@ -53,7 +53,7 @@ describe('CurrentAccount class', () => {

context = dsMockUtils.getContextInstance({ primaryKey: address });

const account = new CurrentAccount({ address: 'someAddress' }, context);
const account = new CurrentAccount({ address }, context);

const result = await account.getPermissions();

Expand Down Expand Up @@ -81,11 +81,60 @@ describe('CurrentAccount class', () => {
],
});

const account = new CurrentAccount({ address: 'someAddress' }, context);
const account = new CurrentAccount({ address }, context);

const result = await account.getPermissions();

expect(result).toEqual(permissions);
});
});

describe('method: hasPermissions', () => {
test('should return whether the Account has the passed permissions', async () => {
const address = 'someAddress';

context = dsMockUtils.getContextInstance({ primaryKey: address });

let account = new CurrentAccount({ address }, context);

let result = await account.hasPermissions({ tokens: [], portfolios: [], transactions: [] });

expect(result).toEqual(true);

let permissions: Permissions = { tokens: [], transactions: [], portfolios: [] };
context = dsMockUtils.getContextInstance({
secondaryKeys: [{ signer: entityMockUtils.getAccountInstance({ address }), permissions }],
});

account = new CurrentAccount({ address }, context);

result = await account.hasPermissions({ tokens: [], portfolios: [], transactions: [] });

expect(result).toEqual(true);

result = await account.hasPermissions({ tokens: null, portfolios: null, transactions: null });

expect(result).toEqual(false);

const token = entityMockUtils.getSecurityTokenInstance({ ticker: 'SOME_TOKEN' });
permissions = {
tokens: [token],
transactions: [TxTags.asset.CreateAsset],
portfolios: [entityMockUtils.getDefaultPortfolioInstance({ did: 'someDid' })],
};
context = dsMockUtils.getContextInstance({
secondaryKeys: [{ signer: entityMockUtils.getAccountInstance({ address }), permissions }],
});

account = new CurrentAccount({ address }, context);

result = await account.hasPermissions({
tokens: [token],
portfolios: [entityMockUtils.getDefaultPortfolioInstance({ did: 'otherDid' })],
transactions: [TxTags.asset.CreateAsset],
});

expect(result).toEqual(false);
});
});
});
8 changes: 4 additions & 4 deletions src/api/entities/__tests__/CurrentIdentity.ts
Expand Up @@ -185,11 +185,11 @@ describe('CurrentIdentity class', () => {
);

portfolioLikeToPortfolioIdStub
.withArgs(defaultPortfolio, context)
.resolves({ did: defaultPortfolioDid, number: undefined });
.withArgs(defaultPortfolio)
.returns({ did: defaultPortfolioDid, number: undefined });
portfolioLikeToPortfolioIdStub
.withArgs(numberedPortfolio, context)
.resolves({ did: numberedPortfolioDid, number: numberedPortfolioId });
.withArgs(numberedPortfolio)
.returns({ did: numberedPortfolioDid, number: numberedPortfolioId });

const rawPortfolio = dsMockUtils.createMockPortfolioId({
did: dsMockUtils.createMockIdentityId(did),
Expand Down

0 comments on commit eefb544

Please sign in to comment.