Skip to content

Commit d04ceee

Browse files
authored
Merge pull request #684 from nats-io/conn-ctx
[INTERNAL] mechanism to retrieve connection info
2 parents 8b2843c + bcad9c6 commit d04ceee

File tree

5 files changed

+156
-9
lines changed

5 files changed

+156
-9
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ The following is the list of connection options and default values.
811811
### TlsOptions
812812

813813
| Option | Default | Description |
814-
| ---------------- | ------- |---------------------------------------------------------------------------------------------------------------------------------|
814+
| ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |
815815
| `ca` | N/A | CA certificate |
816816
| `caFile` | | CA certificate filepath |
817817
| `cert` | N/A | Client certificate |

nats-base-client/core.ts

+61
Original file line numberDiff line numberDiff line change
@@ -1434,3 +1434,64 @@ export enum ServiceVerb {
14341434
STATS = "STATS",
14351435
INFO = "INFO",
14361436
}
1437+
1438+
export type Context = {
1439+
server: ContextServer;
1440+
data: ContextUser;
1441+
};
1442+
1443+
export type ContextServer = {
1444+
name: string;
1445+
host: string;
1446+
id: string;
1447+
version: string;
1448+
tags: string[];
1449+
jetstream: boolean;
1450+
flags: number;
1451+
seq: number;
1452+
time: Date;
1453+
};
1454+
1455+
export type ContextUser = {
1456+
user: string;
1457+
account: string;
1458+
permissions?: {
1459+
publish?: ContextPermission;
1460+
subscribe?: ContextPermission;
1461+
responses?: ContextResponsePermission;
1462+
};
1463+
};
1464+
1465+
export type ContextPermission = {
1466+
deny?: string[];
1467+
allow?: string[];
1468+
};
1469+
1470+
export type ContextResponsePermission = {
1471+
max: number;
1472+
ttl: number;
1473+
};
1474+
1475+
export type RequestInfo = {
1476+
acc: string;
1477+
rtt: number;
1478+
start?: Date;
1479+
host?: string;
1480+
id?: string;
1481+
svc?: string;
1482+
user?: string;
1483+
name?: string;
1484+
lang?: string;
1485+
ver?: string;
1486+
server?: string;
1487+
cluster?: string;
1488+
alts?: string[];
1489+
stop?: Date;
1490+
jwt?: string;
1491+
issuer_key?: string;
1492+
name_tag?: string;
1493+
tags?: string[];
1494+
client_type?: string;
1495+
client_id?: string;
1496+
nonce?: string;
1497+
};

nats-base-client/msg.ts

+17
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
MsgHdrs,
2323
NatsError,
2424
Publisher,
25+
RequestInfo,
2526
ReviverFn,
2627
} from "./core.ts";
2728

@@ -113,4 +114,20 @@ export class MsgImpl implements Msg {
113114
string(): string {
114115
return TD.decode(this.data);
115116
}
117+
118+
requestInfo(): RequestInfo | null {
119+
const v = this.headers?.get("Nats-Request-Info");
120+
if (v) {
121+
return JSON.parse(
122+
v,
123+
function (this: unknown, key: string, value: unknown): unknown {
124+
if ((key === "start" || key === "stop") && value !== "") {
125+
return new Date(Date.parse(value as string));
126+
}
127+
return value;
128+
},
129+
) as RequestInfo;
130+
}
131+
return null;
132+
}
116133
}

nats-base-client/nats.ts

+11
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { ServiceClientImpl } from "./serviceclient.ts";
3636
import { JetStreamClient, JetStreamManager } from "../jetstream/types.ts";
3737
import {
3838
ConnectionOptions,
39+
Context,
3940
createInbox,
4041
ErrorCode,
4142
JetStreamManagerOptions,
@@ -477,6 +478,16 @@ export class NatsConnectionImpl implements NatsConnection {
477478
return this.protocol.isClosed() ? undefined : this.protocol.info;
478479
}
479480

481+
async context(): Promise<Context> {
482+
const r = await this.request(`$SYS.REQ.USER.INFO`);
483+
return r.json<Context>((key, value) => {
484+
if (key === "time") {
485+
return new Date(Date.parse(value));
486+
}
487+
return value;
488+
});
489+
}
490+
480491
stats(): Stats {
481492
return {
482493
inBytes: this.protocol.inBytes,

tests/auth_test.ts

+66-8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
import {
17+
assertArrayIncludes,
1718
assertEquals,
1819
assertRejects,
1920
fail,
@@ -39,6 +40,7 @@ import {
3940
import { assertErrorCode, cleanup, NatsServer, setup } from "./helpers/mod.ts";
4041
import {
4142
deferred,
43+
MsgImpl,
4244
NatsConnectionImpl,
4345
nkeys,
4446
} from "../nats-base-client/internal_mod.ts";
@@ -144,11 +146,11 @@ Deno.test("auth - sub no permissions keeps connection", async () => {
144146
}, { user: "a", pass: "a", reconnect: false });
145147

146148
const errStatus = deferred<Status>();
147-
const _ = (async () => {
149+
(async () => {
148150
for await (const s of nc.status()) {
149151
errStatus.resolve(s);
150152
}
151-
})();
153+
})().then();
152154

153155
const cbErr = deferred<Error | null>();
154156
const sub = nc.subscribe("bar", {
@@ -180,11 +182,11 @@ Deno.test("auth - sub iterator no permissions keeps connection", async () => {
180182
}, { user: "a", pass: "a", reconnect: false });
181183

182184
const errStatus = deferred<Status>();
183-
const _ = (async () => {
185+
(async () => {
184186
for await (const s of nc.status()) {
185187
errStatus.resolve(s);
186188
}
187-
})();
189+
})().then();
188190

189191
const iterErr = deferred<Error | null>();
190192
const sub = nc.subscribe("bar");
@@ -222,11 +224,11 @@ Deno.test("auth - pub permissions keep connection", async () => {
222224
}, { user: "a", pass: "a", reconnect: false });
223225

224226
const errStatus = deferred<Status>();
225-
const _ = (async () => {
227+
(async () => {
226228
for await (const s of nc.status()) {
227229
errStatus.resolve(s);
228230
}
229-
})();
231+
})().then();
230232

231233
nc.publish("bar");
232234

@@ -249,11 +251,11 @@ Deno.test("auth - req permissions keep connection", async () => {
249251
}, { user: "a", pass: "a", reconnect: false });
250252

251253
const errStatus = deferred<Status>();
252-
const _ = (async () => {
254+
(async () => {
253255
for await (const s of nc.status()) {
254256
errStatus.resolve(s);
255257
}
256-
})();
258+
})().then();
257259

258260
const err = await assertRejects(
259261
async () => {
@@ -1194,3 +1196,59 @@ Deno.test("auth - creds and un and pw and token", async () => {
11941196
await nc.close();
11951197
await ns.stop();
11961198
});
1199+
1200+
Deno.test("auth - request context", async () => {
1201+
const { ns, nc } = await setup({
1202+
accounts: {
1203+
S: {
1204+
users: [{
1205+
user: "s",
1206+
password: "s",
1207+
permission: {
1208+
subscribe: ["q.>", "_INBOX.>"],
1209+
publish: "$SYS.REQ.USER.INFO",
1210+
allow_responses: true,
1211+
},
1212+
}],
1213+
exports: [
1214+
{ service: "q.>" },
1215+
],
1216+
},
1217+
A: {
1218+
users: [{ user: "a", password: "a" }],
1219+
imports: [
1220+
{ service: { subject: "q.>", account: "S" } },
1221+
],
1222+
},
1223+
},
1224+
}, { user: "s", pass: "s" });
1225+
1226+
const srv = await (nc as NatsConnectionImpl).context();
1227+
assertEquals(srv.data.user, "s");
1228+
assertEquals(srv.data.account, "S");
1229+
assertArrayIncludes(srv.data.permissions?.publish?.allow || [], [
1230+
"$SYS.REQ.USER.INFO",
1231+
]);
1232+
assertArrayIncludes(srv.data.permissions?.subscribe?.allow || [], [
1233+
"q.>",
1234+
"_INBOX.>",
1235+
]);
1236+
assertEquals(srv.data.permissions?.responses?.max, 1);
1237+
1238+
nc.subscribe("q.>", {
1239+
callback(err, msg) {
1240+
if (err) {
1241+
fail(err.message);
1242+
}
1243+
const info = (msg as MsgImpl).requestInfo();
1244+
assertEquals(info?.acc, "A");
1245+
msg.respond();
1246+
},
1247+
});
1248+
1249+
const a = await connect({ user: "a", pass: "a", port: ns.port });
1250+
console.log(await (a as NatsConnectionImpl).context());
1251+
await a.request("q.hello");
1252+
1253+
await cleanup(ns, nc, a);
1254+
});

0 commit comments

Comments
 (0)