Skip to content
This repository has been archived by the owner on Feb 8, 2024. It is now read-only.

Commit

Permalink
Set networks per-key (#105)
Browse files Browse the repository at this point in the history
- Add a field to the `Key` type, `network`. When absent, assume the key to be Networks.PUBLIC.
- Remove the deprecated methods for setting Network.
- When checking a challenge response, check that the network passphrase matches the key's network.
- If the challenge doesn't include a network passphrase, ignore it.
- Bump to v0.0.5-rc.1.
  • Loading branch information
Morley Zhi authored Sep 17, 2019
1 parent 668f45b commit 8bfa711
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 10 deletions.
1 change: 1 addition & 0 deletions config/polyfills.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
global.regeneratorRuntime = require("regenerator-runtime");
global.fetch = require("jest-fetch-mock");
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
module.exports = {
// Automatically clear mock calls and instances between every test
clearMocks: true,
automock: false,

setupFiles: ["<rootDir>/config/polyfills.js"],

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stellar/wallet-sdk",
"version": "0.0.4-rc.4",
"version": "0.0.5-rc.1",
"description": "Libraries to help you write Stellar-enabled wallets in Javascript",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -53,6 +53,7 @@
"concurrently": "^4.1.1",
"husky": "^1.3.1",
"jest": "^24.7.1",
"jest-fetch-mock": "^2.1.2",
"lint-staged": "^8.1.5",
"prettier": "^1.17.0",
"regenerator-runtime": "^0.13.2",
Expand Down
2 changes: 0 additions & 2 deletions playground/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import Offers from "components/Offers";
import Trades from "components/Trades";
import Transfers from "components/Transfers";

StellarSdk.Network.usePublicNetwork();

const El = styled.div`
display: flex;
font-size: 0.8em;
Expand Down
130 changes: 126 additions & 4 deletions src/KeyManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,19 @@ describe("KeyManager", function() {
const testKeyManager = new KeyManager({
keyStore: testStore,
});
const network = StellarBase.Networks.TESTNET;

testKeyManager.registerEncrypter(IdentityEncrypter);

StellarBase.Network.useTestNetwork();

const keypair = StellarBase.Keypair.master();
const keypair = StellarBase.Keypair.master(network);

// save this key
const keyMetadata = await testKeyManager.storeKey({
key: {
type: KeyType.plaintextKey,
publicKey: keypair.publicKey(),
privateKey: keypair.secret(),
network,
},
password: "test",
encrypterName: "IdentityEncrypter",
Expand All @@ -84,7 +84,10 @@ describe("KeyManager", function() {
"0",
);

const transaction = new StellarBase.TransactionBuilder(source, { fee: 100 })
const transaction = new StellarBase.TransactionBuilder(source, {
fee: 100,
networkPassphrase: network,
})
.addOperation(StellarBase.Operation.inflation({}))
.setTimeout(StellarBase.TimeoutInfinite)
.build();
Expand All @@ -102,4 +105,123 @@ describe("KeyManager", function() {
),
).toEqual(true);
});

describe("getAuthToken", () => {
beforeEach(() => {
// @ts-ignore
fetch.resetMocks();
});

test("Throws errors for missing params", async () => {
// set up the manager
const testStore = new MemoryKeyStore();
const testKeyManager = new KeyManager({
keyStore: testStore,
});
try {
// @ts-ignore
await testKeyManager.getAuthToken({});
expect("This test failed").toBe(null);
} catch (e) {
expect(e).toBeTruthy();
}
});

test("Rejects challenges that return errors", async () => {
const authServer = "https://www.stellar.org/auth";
const error = "Test error";
const password = "very secure password";

// @ts-ignore
fetch.mockResponseOnce(
JSON.stringify({
error,
}),
);

// set up the manager
const testStore = new MemoryKeyStore();
const testKeyManager = new KeyManager({
keyStore: testStore,
});
const network = StellarBase.Networks.TESTNET;

testKeyManager.registerEncrypter(IdentityEncrypter);

const keypair = StellarBase.Keypair.master(network);

// save this key
const keyMetadata = await testKeyManager.storeKey({
key: {
type: KeyType.plaintextKey,
publicKey: keypair.publicKey(),
privateKey: keypair.secret(),
network,
},
password,
encrypterName: "IdentityEncrypter",
});

try {
await testKeyManager.getAuthToken({
id: keyMetadata.id,
password,
authServer,
});

expect("This test failed").toBe(null);
} catch (e) {
expect(e.toString()).toBe(`Error: ${error}`);
}
});

test("Rejects challenges with network mismatches", async () => {
const authServer = "https://www.stellar.org/auth";
const password = "very secure password";

const keyNetwork = StellarBase.Networks.TESTNET;
const challengeNetwork = StellarBase.Networks.PUBLIC;

// @ts-ignore
fetch.mockResponseOnce(
JSON.stringify({
network_passphrase: challengeNetwork,
}),
);

// set up the manager
const testStore = new MemoryKeyStore();
const testKeyManager = new KeyManager({
keyStore: testStore,
});

testKeyManager.registerEncrypter(IdentityEncrypter);

const keypair = StellarBase.Keypair.master(keyNetwork);

// save this key
const keyMetadata = await testKeyManager.storeKey({
key: {
type: KeyType.plaintextKey,
publicKey: keypair.publicKey(),
privateKey: keypair.secret(),
network: keyNetwork,
},
password,
encrypterName: "IdentityEncrypter",
});

try {
await testKeyManager.getAuthToken({
id: keyMetadata.id,
password,
authServer,
});

expect("This test failed").toBe(null);
} catch (e) {
expect(e.toString()).toMatch(`Network mismatch`);
}
});
});
});
29 changes: 26 additions & 3 deletions src/KeyManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sha1 from "js-sha1";
import { Network, Networks, Transaction } from "stellar-sdk";
import { Networks, Transaction } from "stellar-sdk";

import { ledgerHandler } from "./keyTypeHandlers/ledger";
import { plaintextKeyHandler } from "./keyTypeHandlers/plaintextKey";
Expand All @@ -20,6 +20,7 @@ import {

export interface KeyManagerParams {
keyStore: KeyStore;
defaultNetworkPassphrase?: string;
shouldCache?: boolean;
}

Expand Down Expand Up @@ -79,6 +80,7 @@ export class KeyManager {
private keyHandlerMap: { [key: string]: KeyTypeHandler };
private keyCache: { [id: string]: Key };
private shouldCache: boolean;
private defaultNetworkPassphrase: string;

constructor(params: KeyManagerParams) {
this.encrypterMap = {};
Expand All @@ -91,12 +93,23 @@ export class KeyManager {

this.keyStore = params.keyStore;
this.shouldCache = params.shouldCache || false;

this.defaultNetworkPassphrase =
params.defaultNetworkPassphrase || Networks.PUBLIC;
}

/**
* Register a KeyTypeHandler for a given key type.
* @param {KeyTypeHandler} keyHandler
*/
public registerKeyHandler(keyHandler: KeyTypeHandler) {
this.keyHandlerMap[keyHandler.keyType] = keyHandler;
}

/**
* Register a new encrypter.
* @param {Encrypter} encrypter
*/
public registerEncrypter(encrypter: Encrypter) {
this.encrypterMap[encrypter.name] = encrypter;
}
Expand Down Expand Up @@ -263,6 +276,8 @@ export class KeyManager {
`${authServer}?account=${encodeURIComponent(key.publicKey)}`,
);

const keyNetwork = key.network || this.defaultNetworkPassphrase;

const {
transaction,
network_passphrase,
Expand All @@ -273,12 +288,20 @@ export class KeyManager {
throw new Error(error);
}

Network.use(new Network(network_passphrase || Networks.PUBLIC));
// Throw error when network_passphrase is returned, and doesn't match
if (network_passphrase !== undefined && keyNetwork !== network_passphrase) {
throw new Error(
`
Network mismatch: the transfer server expects "${network_passphrase}",
but you're using "${keyNetwork}"
`,
);
}

const keyHandler = this.keyHandlerMap[key.type];

const signedTransaction = await keyHandler.signTransaction({
transaction: new Transaction(transaction),
transaction: new Transaction(transaction, keyNetwork),
key,
});

Expand Down
2 changes: 2 additions & 0 deletions src/types/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export interface BaseKey {
extra?: any;
path?: string;
publicKey: string;
// if the network is not set, the key is assumed to be on Networks.PUBLIC
network?: string;
type: KeyType | string;
}

Expand Down
31 changes: 31 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,14 @@ crc@^3.5.0:
dependencies:
buffer "^5.1.0"

cross-fetch@^2.2.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e"
integrity sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw==
dependencies:
node-fetch "2.1.2"
whatwg-fetch "2.0.4"

cross-spawn@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
Expand Down Expand Up @@ -3120,6 +3128,14 @@ jest-environment-node@^24.7.1:
jest-mock "^24.7.0"
jest-util "^24.7.1"

jest-fetch-mock@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-2.1.2.tgz#1260b347918e3931c4ec743ceaf60433da661bd0"
integrity sha512-tcSR4Lh2bWLe1+0w/IwvNxeDocMI/6yIA2bijZ0fyWxC4kQ18lckQ1n7Yd40NKuisGmcGBRFPandRXrW/ti/Bw==
dependencies:
cross-fetch "^2.2.2"
promise-polyfill "^7.1.1"

jest-get-type@^24.3.0:
version "24.3.0"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.3.0.tgz#582cfd1a4f91b5cdad1d43d2932f816d543c65da"
Expand Down Expand Up @@ -3973,6 +3989,11 @@ node-cleanup@^2.1.2:
resolved "https://registry.yarnpkg.com/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c"
integrity sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=

[email protected]:
version "2.1.2"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5"
integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=

node-gyp-build@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.0.tgz#3bc3dd7dd4aafecaf64a2e3729e785bc3cdea565"
Expand Down Expand Up @@ -4452,6 +4473,11 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==

promise-polyfill@^7.1.1:
version "7.1.2"
resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-7.1.2.tgz#ab05301d8c28536301622d69227632269a70ca3b"
integrity sha512-FuEc12/eKqqoRYIGBrUptCBRhobL19PS2U31vMNTfyck1FxPyMfgsXyW4Mav85y/ZN1hop3hOwRlUDok23oYfQ==

prompts@^2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.0.4.tgz#179f9d4db3128b9933aa35f93a800d8fce76a682"
Expand Down Expand Up @@ -5728,6 +5754,11 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
dependencies:
iconv-lite "0.4.24"

[email protected]:
version "2.0.4"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==

whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
Expand Down

0 comments on commit 8bfa711

Please sign in to comment.