Wyle.Gong-巩文昕 7a1aae1e2f ui
2025-04-23 11:21:08 +08:00

417 lines
15 KiB
JavaScript

/*
* Copyright 2018-2023 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const test = require("ava");
const {
connect,
ErrorCode,
jwtAuthenticator,
nkeyAuthenticator,
credsAuthenticator,
StringCodec,
deferred,
} = require(
"./index",
);
const { nkeys, Events } = require("../lib/nats-base-client/internal_mod");
const { NatsServer, wsConfig } = require("./helpers/launcher");
const {
createOperator,
createAccount,
encodeAccount,
encodeOperator,
encodeUser,
createUser,
} = require("nats-jwt");
const conf = Object.assign({
authorization: {
PERM: {
subscribe: "bar",
publish: "foo",
},
users: [{
user: "derek",
password: "foobar",
permission: "$PERM",
}],
},
}, wsConfig());
test("auth - none", async (t) => {
t.plan(1);
const ns = await NatsServer.start(conf);
try {
const nc = await connect({ servers: `ws://127.0.0.1:${ns.websocket}` });
await nc.close();
t.fail("shouldnt have been able to connect");
} catch (ex) {
t.is(ex.code, ErrorCode.AuthorizationViolation);
}
await ns.stop();
});
test("auth - bad", async (t) => {
t.plan(1);
const ns = await NatsServer.start(conf);
try {
const nc = await connect(
{ servers: `ws://127.0.0.1:${ns.websocket}`, user: "me", pass: "hello" },
);
await nc.close();
t.fail("shouldnt have been able to connect");
} catch (ex) {
t.is(ex.code, ErrorCode.AuthorizationViolation);
}
await ns.stop();
});
test("auth - un/pw", async (t) => {
t.plan(1);
const ns = await NatsServer.start(conf);
const nc = await connect(
{
servers: `ws://127.0.0.1:${ns.websocket}`,
user: "derek",
pass: "foobar",
},
);
await nc.flush();
await nc.close();
await ns.stop();
t.pass();
});
test("auth - sub permissions", async (t) => {
t.plan(4);
const ns = await NatsServer.start(conf);
const nc = await connect(
{
servers: `ws://127.0.0.1:${ns.websocket}`,
user: "derek",
pass: "foobar",
},
);
const errStatus = deferred();
const _ = (async () => {
for await (const s of nc.status()) {
errStatus.resolve(s);
}
})();
const iterErr = deferred();
const sub = nc.subscribe("foo");
(async () => {
for await (const m of sub) {
}
})().catch((err) => {
iterErr.resolve(err);
});
const v = await Promise.all([errStatus, iterErr, sub.closed]);
t.is(v[0].data, ErrorCode.PermissionsViolation);
t.is(v[1].message, "'Permissions Violation for Subscription to \"foo\"'");
t.true(sub.isClosed());
t.false(nc.isClosed());
await nc.close();
await ns.stop();
});
test("auth - weird characters", async (t) => {
const pass = "§12§12§12";
const conf = Object.assign({
authorization: {
username: "admin",
password: pass,
},
}, wsConfig());
const ns = await NatsServer.start(conf);
const nc = await connect({
servers: [`ws://127.0.0.1:${ns.websocket}`],
user: "admin",
pass: pass,
});
await nc.flush;
await nc.close();
await ns.stop();
t.pass();
});
test("auth - pub perm", async (t) => {
t.plan(2);
const ns = await NatsServer.start(conf);
const nc = await connect(
{
servers: `ws://127.0.0.1:${ns.websocket}`,
user: "derek",
pass: "foobar",
},
);
const errStatus = deferred();
const _ = (async () => {
for await (const s of nc.status()) {
errStatus.resolve(s);
}
})();
nc.publish("bar");
const v = await errStatus;
t.is(v.data, ErrorCode.PermissionsViolation);
t.false(nc.isClosed());
await nc.close();
await ns.stop();
});
test("auth - token", async (t) => {
const ns = await NatsServer.start(
Object.assign({ authorization: { token: "foo" } }, wsConfig()),
);
const nc = await connect(
{ servers: `ws://127.0.0.1:${ns.websocket}`, token: "foo" },
);
await nc.flush();
await nc.close();
await ns.stop();
t.pass();
});
test("auth - nkey", async (t) => {
t.plan(1);
const kp = nkeys.createUser();
const pk = kp.getPublicKey();
const seed = kp.getSeed();
const conf = Object.assign({
authorization: {
users: [
{ nkey: pk },
],
},
}, wsConfig());
const ns = await NatsServer.start(conf);
const nc = await connect(
{
servers: `ws://127.0.0.1:${ns.websocket}`,
authenticator: nkeyAuthenticator(seed),
},
);
await nc.flush();
await nc.close();
await ns.stop();
t.pass();
});
test("auth - creds", async (t) => {
const creds = `-----BEGIN NATS USER JWT-----
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJFU1VQS1NSNFhGR0pLN0FHUk5ZRjc0STVQNTZHMkFGWERYQ01CUUdHSklKUEVNUVhMSDJBIiwiaWF0IjoxNTQ0MjE3NzU3LCJpc3MiOiJBQ1pTV0JKNFNZSUxLN1FWREVMTzY0VlgzRUZXQjZDWENQTUVCVUtBMzZNSkpRUlBYR0VFUTJXSiIsInN1YiI6IlVBSDQyVUc2UFY1NTJQNVNXTFdUQlAzSDNTNUJIQVZDTzJJRUtFWFVBTkpYUjc1SjYzUlE1V002IiwidHlwZSI6InVzZXIiLCJuYXRzIjp7InB1YiI6e30sInN1YiI6e319fQ.kCR9Erm9zzux4G6M-V2bp7wKMKgnSNqMBACX05nwePRWQa37aO_yObbhcJWFGYjo1Ix-oepOkoyVLxOJeuD8Bw
------END NATS USER JWT------
************************* IMPORTANT *************************
NKEY Seed printed below can be used sign and prove identity.
NKEYs are sensitive and should be treated as secrets.
-----BEGIN USER NKEY SEED-----
SUAIBDPBAUTWCWBKIO6XHQNINK5FWJW4OHLXC3HQ2KFE4PEJUA44CNHTC4
------END USER NKEY SEED------
`;
const conf = Object.assign({
operator:
"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJhdWQiOiJURVNUUyIsImV4cCI6MTg1OTEyMTI3NSwianRpIjoiWE5MWjZYWVBIVE1ESlFSTlFPSFVPSlFHV0NVN01JNVc1SlhDWk5YQllVS0VRVzY3STI1USIsImlhdCI6MTU0Mzc2MTI3NSwiaXNzIjoiT0NBVDMzTVRWVTJWVU9JTUdOR1VOWEo2NkFIMlJMU0RBRjNNVUJDWUFZNVFNSUw2NU5RTTZYUUciLCJuYW1lIjoiU3luYWRpYSBDb21tdW5pY2F0aW9ucyBJbmMuIiwibmJmIjoxNTQzNzYxMjc1LCJzdWIiOiJPQ0FUMzNNVFZVMlZVT0lNR05HVU5YSjY2QUgyUkxTREFGM01VQkNZQVk1UU1JTDY1TlFNNlhRRyIsInR5cGUiOiJvcGVyYXRvciIsIm5hdHMiOnsic2lnbmluZ19rZXlzIjpbIk9EU0tSN01ZRlFaNU1NQUo2RlBNRUVUQ1RFM1JJSE9GTFRZUEpSTUFWVk40T0xWMllZQU1IQ0FDIiwiT0RTS0FDU1JCV1A1MzdEWkRSVko2NTdKT0lHT1BPUTZLRzdUNEhONk9LNEY2SUVDR1hEQUhOUDIiLCJPRFNLSTM2TFpCNDRPWTVJVkNSNlA1MkZaSlpZTVlXWlZXTlVEVExFWjVUSzJQTjNPRU1SVEFCUiJdfX0.hyfz6E39BMUh0GLzovFfk3wT4OfualftjdJ_eYkLfPvu5tZubYQ_Pn9oFYGCV_6yKy3KMGhWGUCyCdHaPhalBw",
resolver: "MEMORY",
resolver_preload: {
ACZSWBJ4SYILK7QVDELO64VX3EFWB6CXCPMEBUKA36MJJQRPXGEEQ2WJ:
"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJXVFdYVDNCT1JWSFNLQkc2T0pIVVdFQ01QRVdBNldZVEhNRzVEWkJBUUo1TUtGU1dHM1FRIiwiaWF0IjoxNTQ0MjE3NzU3LCJpc3MiOiJPQ0FUMzNNVFZVMlZVT0lNR05HVU5YSjY2QUgyUkxTREFGM01VQkNZQVk1UU1JTDY1TlFNNlhRRyIsInN1YiI6IkFDWlNXQko0U1lJTEs3UVZERUxPNjRWWDNFRldCNkNYQ1BNRUJVS0EzNk1KSlFSUFhHRUVRMldKIiwidHlwZSI6ImFjY291bnQiLCJuYXRzIjp7ImxpbWl0cyI6eyJzdWJzIjotMSwiY29ubiI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xLCJ3aWxkY2FyZHMiOnRydWV9fX0.q-E7bBGTU0uoTmM9Vn7WaEHDzCUrqvPDb9mPMQbry_PNzVAjf0RG9vd15lGxW5lu7CuGVqpj4CYKhNDHluIJAg",
},
}, wsConfig());
const ns = await NatsServer.start(conf);
const nc = await connect(
{
servers: `ws://127.0.0.1:${ns.websocket}`,
authenticator: credsAuthenticator(new TextEncoder().encode(creds)),
},
);
await nc.flush();
await nc.close();
await ns.stop();
t.pass();
});
test("auth - custom", async (t) => {
const jwt =
"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJFU1VQS1NSNFhGR0pLN0FHUk5ZRjc0STVQNTZHMkFGWERYQ01CUUdHSklKUEVNUVhMSDJBIiwiaWF0IjoxNTQ0MjE3NzU3LCJpc3MiOiJBQ1pTV0JKNFNZSUxLN1FWREVMTzY0VlgzRUZXQjZDWENQTUVCVUtBMzZNSkpRUlBYR0VFUTJXSiIsInN1YiI6IlVBSDQyVUc2UFY1NTJQNVNXTFdUQlAzSDNTNUJIQVZDTzJJRUtFWFVBTkpYUjc1SjYzUlE1V002IiwidHlwZSI6InVzZXIiLCJuYXRzIjp7InB1YiI6e30sInN1YiI6e319fQ.kCR9Erm9zzux4G6M-V2bp7wKMKgnSNqMBACX05nwePRWQa37aO_yObbhcJWFGYjo1Ix-oepOkoyVLxOJeuD8Bw";
const useed = "SUAIBDPBAUTWCWBKIO6XHQNINK5FWJW4OHLXC3HQ2KFE4PEJUA44CNHTC4";
const conf = Object.assign({
operator:
"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJhdWQiOiJURVNUUyIsImV4cCI6MTg1OTEyMTI3NSwianRpIjoiWE5MWjZYWVBIVE1ESlFSTlFPSFVPSlFHV0NVN01JNVc1SlhDWk5YQllVS0VRVzY3STI1USIsImlhdCI6MTU0Mzc2MTI3NSwiaXNzIjoiT0NBVDMzTVRWVTJWVU9JTUdOR1VOWEo2NkFIMlJMU0RBRjNNVUJDWUFZNVFNSUw2NU5RTTZYUUciLCJuYW1lIjoiU3luYWRpYSBDb21tdW5pY2F0aW9ucyBJbmMuIiwibmJmIjoxNTQzNzYxMjc1LCJzdWIiOiJPQ0FUMzNNVFZVMlZVT0lNR05HVU5YSjY2QUgyUkxTREFGM01VQkNZQVk1UU1JTDY1TlFNNlhRRyIsInR5cGUiOiJvcGVyYXRvciIsIm5hdHMiOnsic2lnbmluZ19rZXlzIjpbIk9EU0tSN01ZRlFaNU1NQUo2RlBNRUVUQ1RFM1JJSE9GTFRZUEpSTUFWVk40T0xWMllZQU1IQ0FDIiwiT0RTS0FDU1JCV1A1MzdEWkRSVko2NTdKT0lHT1BPUTZLRzdUNEhONk9LNEY2SUVDR1hEQUhOUDIiLCJPRFNLSTM2TFpCNDRPWTVJVkNSNlA1MkZaSlpZTVlXWlZXTlVEVExFWjVUSzJQTjNPRU1SVEFCUiJdfX0.hyfz6E39BMUh0GLzovFfk3wT4OfualftjdJ_eYkLfPvu5tZubYQ_Pn9oFYGCV_6yKy3KMGhWGUCyCdHaPhalBw",
resolver: "MEMORY",
resolver_preload: {
ACZSWBJ4SYILK7QVDELO64VX3EFWB6CXCPMEBUKA36MJJQRPXGEEQ2WJ:
"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJXVFdYVDNCT1JWSFNLQkc2T0pIVVdFQ01QRVdBNldZVEhNRzVEWkJBUUo1TUtGU1dHM1FRIiwiaWF0IjoxNTQ0MjE3NzU3LCJpc3MiOiJPQ0FUMzNNVFZVMlZVT0lNR05HVU5YSjY2QUgyUkxTREFGM01VQkNZQVk1UU1JTDY1TlFNNlhRRyIsInN1YiI6IkFDWlNXQko0U1lJTEs3UVZERUxPNjRWWDNFRldCNkNYQ1BNRUJVS0EzNk1KSlFSUFhHRUVRMldKIiwidHlwZSI6ImFjY291bnQiLCJuYXRzIjp7ImxpbWl0cyI6eyJzdWJzIjotMSwiY29ubiI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xLCJ3aWxkY2FyZHMiOnRydWV9fX0.q-E7bBGTU0uoTmM9Vn7WaEHDzCUrqvPDb9mPMQbry_PNzVAjf0RG9vd15lGxW5lu7CuGVqpj4CYKhNDHluIJAg",
},
}, wsConfig());
const ns = await NatsServer.start(conf);
const authenticator = (nonce) => {
const seed = nkeys.fromSeed(new TextEncoder().encode(useed));
const nkey = seed.getPublicKey();
const hash = seed.sign(new TextEncoder().encode(nonce));
const sig = nkeys.encode(hash);
return { nkey, sig, jwt };
};
const nc = await connect(
{
servers: `ws://127.0.0.1:${ns.websocket}`,
authenticator: authenticator,
},
);
await nc.flush();
await nc.close();
await ns.stop();
t.pass();
});
test("auth - jwt", async (t) => {
const jwt =
"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJFU1VQS1NSNFhGR0pLN0FHUk5ZRjc0STVQNTZHMkFGWERYQ01CUUdHSklKUEVNUVhMSDJBIiwiaWF0IjoxNTQ0MjE3NzU3LCJpc3MiOiJBQ1pTV0JKNFNZSUxLN1FWREVMTzY0VlgzRUZXQjZDWENQTUVCVUtBMzZNSkpRUlBYR0VFUTJXSiIsInN1YiI6IlVBSDQyVUc2UFY1NTJQNVNXTFdUQlAzSDNTNUJIQVZDTzJJRUtFWFVBTkpYUjc1SjYzUlE1V002IiwidHlwZSI6InVzZXIiLCJuYXRzIjp7InB1YiI6e30sInN1YiI6e319fQ.kCR9Erm9zzux4G6M-V2bp7wKMKgnSNqMBACX05nwePRWQa37aO_yObbhcJWFGYjo1Ix-oepOkoyVLxOJeuD8Bw";
const useed = "SUAIBDPBAUTWCWBKIO6XHQNINK5FWJW4OHLXC3HQ2KFE4PEJUA44CNHTC4";
const conf = Object.assign({
operator:
"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJhdWQiOiJURVNUUyIsImV4cCI6MTg1OTEyMTI3NSwianRpIjoiWE5MWjZYWVBIVE1ESlFSTlFPSFVPSlFHV0NVN01JNVc1SlhDWk5YQllVS0VRVzY3STI1USIsImlhdCI6MTU0Mzc2MTI3NSwiaXNzIjoiT0NBVDMzTVRWVTJWVU9JTUdOR1VOWEo2NkFIMlJMU0RBRjNNVUJDWUFZNVFNSUw2NU5RTTZYUUciLCJuYW1lIjoiU3luYWRpYSBDb21tdW5pY2F0aW9ucyBJbmMuIiwibmJmIjoxNTQzNzYxMjc1LCJzdWIiOiJPQ0FUMzNNVFZVMlZVT0lNR05HVU5YSjY2QUgyUkxTREFGM01VQkNZQVk1UU1JTDY1TlFNNlhRRyIsInR5cGUiOiJvcGVyYXRvciIsIm5hdHMiOnsic2lnbmluZ19rZXlzIjpbIk9EU0tSN01ZRlFaNU1NQUo2RlBNRUVUQ1RFM1JJSE9GTFRZUEpSTUFWVk40T0xWMllZQU1IQ0FDIiwiT0RTS0FDU1JCV1A1MzdEWkRSVko2NTdKT0lHT1BPUTZLRzdUNEhONk9LNEY2SUVDR1hEQUhOUDIiLCJPRFNLSTM2TFpCNDRPWTVJVkNSNlA1MkZaSlpZTVlXWlZXTlVEVExFWjVUSzJQTjNPRU1SVEFCUiJdfX0.hyfz6E39BMUh0GLzovFfk3wT4OfualftjdJ_eYkLfPvu5tZubYQ_Pn9oFYGCV_6yKy3KMGhWGUCyCdHaPhalBw",
resolver: "MEMORY",
resolver_preload: {
ACZSWBJ4SYILK7QVDELO64VX3EFWB6CXCPMEBUKA36MJJQRPXGEEQ2WJ:
"eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJXVFdYVDNCT1JWSFNLQkc2T0pIVVdFQ01QRVdBNldZVEhNRzVEWkJBUUo1TUtGU1dHM1FRIiwiaWF0IjoxNTQ0MjE3NzU3LCJpc3MiOiJPQ0FUMzNNVFZVMlZVT0lNR05HVU5YSjY2QUgyUkxTREFGM01VQkNZQVk1UU1JTDY1TlFNNlhRRyIsInN1YiI6IkFDWlNXQko0U1lJTEs3UVZERUxPNjRWWDNFRldCNkNYQ1BNRUJVS0EzNk1KSlFSUFhHRUVRMldKIiwidHlwZSI6ImFjY291bnQiLCJuYXRzIjp7ImxpbWl0cyI6eyJzdWJzIjotMSwiY29ubiI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xLCJ3aWxkY2FyZHMiOnRydWV9fX0.q-E7bBGTU0uoTmM9Vn7WaEHDzCUrqvPDb9mPMQbry_PNzVAjf0RG9vd15lGxW5lu7CuGVqpj4CYKhNDHluIJAg",
},
}, wsConfig());
const ns = await NatsServer.start(conf);
const nc = await connect(
{
servers: `ws://127.0.0.1:${ns.websocket}`,
authenticator: jwtAuthenticator(jwt, new TextEncoder().encode(useed)),
},
);
await nc.flush();
await nc.close();
await ns.stop();
t.pass();
});
test("auth - custom error", async (t) => {
t.plan(1);
const ns = await NatsServer.start(conf);
const authenticator = () => {
throw new Error("user code exploded");
};
await connect(
{
servers: `ws://127.0.0.1:${ns.websocket}`,
maxReconnectAttempts: 1,
authenticator: authenticator,
},
).then(() => {
t.fail("shouldn't have connected");
}).catch((err) => {
t.is(err.message, "user code exploded");
});
await ns.stop();
});
test("auth - ngs", async (t) => {
t.plan(1);
const token = process.env.WS_NGS_CI_USER || "";
if (token.length === 0) {
t.log("test skipped - no WS_NGS_CI_USER defined in the environment");
t.pass();
return;
} else {
t.log("token.len", token.length);
}
const sc = StringCodec();
const authenticator = jwtAuthenticator(token);
const nc1 = await connect({
servers: "wss://connect.ngs.global",
authenticator: authenticator,
});
const nc2 = await connect({
servers: "wss://connect.ngs.global",
authenticator: authenticator,
});
nc1.subscribe("hello.ngs", {
callback: (err, msg) => {
msg.respond(sc.encode("hi!"));
},
max: 1,
});
await nc1.flush();
const m = await nc2.request("hello.ngs");
t.is(sc.decode(m.data), "hi!");
await nc1.close();
await nc2.close();
});
test("auth - expiration notified", async (t) => {
const O = createOperator();
const A = createAccount();
const resolver = {};
resolver[A.getPublicKey()] = await encodeAccount("A", A, {
limits: {
conn: -1,
subs: -1,
},
}, { signer: O });
const conf = Object.assign({
operator: await encodeOperator("O", O),
resolver: "MEMORY",
"resolver_preload": resolver,
}, wsConfig());
const ns = await NatsServer.start(conf);
const U = createUser();
const ujwt = await encodeUser("U", U, A, { bearer_token: true }, {
exp: Math.round(Date.now() / 1000) + 5,
});
const nc = await connect({
servers: `ws://localhost:${ns.websocket}`,
maxReconnectAttempts: -1,
authenticator: jwtAuthenticator(ujwt),
});
let authErrors = 0;
(async () => {
for await (const s of nc.status()) {
if (
s.type === Events.Error && s.data === ErrorCode.AuthenticationExpired
) {
authErrors++;
}
}
})().then();
const err = await nc.closed();
t.true(authErrors >= 1);
await ns.stop();
});