Skip to content

Commit

Permalink
[FIX] made user have GenericFields - for tags, etc
Browse files Browse the repository at this point in the history
[FEAT] added ability to set `issuer_account` on generic claims
[TEST] added ability to set where nsc stores/reads files for cross lib checks
[DENO] added a deno.json/lock simplifying std imports
[LINT] fixed linter warnings
  • Loading branch information
aricart committed Feb 16, 2023
1 parent c92ef1f commit 77cf256
Show file tree
Hide file tree
Showing 10 changed files with 352 additions and 27 deletions.
4 changes: 2 additions & 2 deletions bin/cjs-fix-imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ requires.set(
);

// resolve the specified directories to fq
let dirs = (argv._ as string[]).map((n) => {
const dirs = (argv._ as string[]).map((n) => {
return resolve(n);
});
// resolve the out dir
Expand Down Expand Up @@ -88,7 +88,7 @@ await Deno.lstat(out)
// process each file - remove extensions from requires/import
for (const fn of files) {
const data = await Deno.readFile(fn);
let txt = new TextDecoder().decode(data);
const txt = new TextDecoder().decode(data);

let mod = txt.replace(/from\s+"(\S+).[t|j]s"/gim, 'from "$1"');
mod = mod.replace(/require\("(\S+).[j|t]s"\)/gim, 'require("$1")');
Expand Down
15 changes: 15 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"imports": {
"std/": "https://deno.land/std@0.177.0/"
},
"lint": {
"files": {
"exclude": ["docs/", "lib/", "esm/jwt.js", "debug/", "cjs/", "cjs_src/"]
}
},
"fmt": {
"files": {
"exclude": ["docs/", "lib/", "esm/jwt.js", "debug/", "cjs/", "cjs_src/"]
}
}
}
127 changes: 127 additions & 0 deletions deno.lock

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion src/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,17 @@ export async function encodeGeneric(
claim.name = name;
claim.nats = data;
claim.sub = akp.getPublicKey();
akp = checkKey(akp, "", !opts.signer);
let signer = akp;
if (opts.signer) {
signer = checkKey(opts.signer, "", true);
}
if (opts.signer) {
claim.nats.issuer_account = akp.getPublicKey();
}
const o = initAlgorithm(opts);
setVersionType(o.algorithm, kind, claim);
return await encode(o.algorithm, claim, akp);
return await encode(o.algorithm, claim, signer);
}

function setVersionType(
Expand Down
4 changes: 2 additions & 2 deletions src/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export * from "./util.ts";
export {
createAccount,
createOperator,
createUser,
createServer,
fromSeed,
createUser,
fromPublic,
fromSeed,
} from "./nkeys.ts";
export type { KeyPair } from "./nkeys.ts";
9 changes: 8 additions & 1 deletion src/nkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ const createUser = nkeys.createUser;
const createServer = nkeys.createServer;
const fromSeed = nkeys.fromSeed;
const fromPublic = nkeys.fromPublic;
export { createAccount, createOperator, createUser, createServer, fromPublic, fromSeed };
export {
createAccount,
createOperator,
createServer,
createUser,
fromPublic,
fromSeed,
};

export interface KeyPair {
getPublicKey(): string;
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export interface UserPermissionsLimits extends Permissions, Limits {
"allowed_connection_types": ConnectionType[];
}

export type User = UserPermissionsLimits & IssuerAccount;
export type User = UserPermissionsLimits & IssuerAccount & GenericFields;

export interface ValidDates {
exp?: number;
Expand Down
34 changes: 27 additions & 7 deletions tests/jwt_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@

import {
assert,
assertArrayIncludes,
assertEquals,
assertExists,
assertRejects,
} from "https://deno.land/std@0.171.0/testing/asserts.ts";
} from "https://deno.land/std/testing/asserts.ts";
import { nsc, parseTable } from "./nsc.ts";
import {
Account,
Expand Down Expand Up @@ -731,18 +732,37 @@ Deno.test("jwt - account disallow_bearer", async () => {

Deno.test("jwt - custom aud", async () => {
const akp = createAccount();
let at = await encodeAccount("A", akp, {}, { aud: "hello" });
let ac = await decode<Account>(at);
const at = await encodeAccount("A", akp, {}, { aud: "hello" });
const ac = await decode<Account>(at);
assertEquals(ac.aud, "hello");

const ukp = createUser();
let ut = await encodeUser("A", ukp, akp, defaultUserLimits(), {
const ut = await encodeUser("A", ukp, akp, defaultUserLimits(), {
aud: "hello",
});
let uc = await decode<User>(ut);
const uc = await decode<User>(ut);
assertEquals(uc.aud, "hello");

let gt = await encodeGeneric("A", akp, "my-kind", {}, { aud: "hello" });
let gc = await decode<unknown>(ut);
const gt = await encodeGeneric("A", akp, "my-kind", {}, { aud: "hello" });
const gc = await decode<unknown>(gt);
assertEquals(gc.aud, "hello");
});

Deno.test("jwt - tags", async () => {
const [o] = await nsc.addOperator();
await nsc.run("add", "account", "A");
await nsc.run("edit", "account", "--tag", "a", "--tag", "b", "--tag", "c");
const ac = await decode<Account>(await nsc.getAccount(o, "A"));
assertExists(ac.nats.tags);
assertArrayIncludes(ac.nats.tags, ["a", "b", "c"]);
ac.nats.tags.push("d");
assertArrayIncludes(ac.nats.tags, ["d"]);

await nsc.run("add", "user", "u");
await nsc.run("edit", "user", "--tag", "x", "--tag", "y", "--tag", "z");
const uc = await decode<User>(await nsc.getUser(o, "A", "u"));
assertExists(uc.nats.tags);
assertArrayIncludes(uc.nats.tags, ["x", "y", "z"]);
uc.nats.tags.push("zz");
assertArrayIncludes(uc.nats.tags, ["zz"]);
});
74 changes: 61 additions & 13 deletions tests/nsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,68 @@
// limitations under the License.

import { dirname, join } from "https://deno.land/std/path/mod.ts";
import { ensureDir } from "https://deno.land/std@0.103.0/fs/mod.ts";
import { ensureDir } from "https://deno.land/std/fs/mod.ts";
import { assert } from "https://deno.land/std/testing/asserts.ts";
import { nuid } from "https://raw.githubusercontent.com/nats-io/nats.deno/main/nats-base-client/nuid.ts";
import type { KeyPair } from "../src/mod.ts";
import { Account, decode, fromSeed, Types } from "../src/mod.ts";
import RunOptions = Deno.RunOptions;

const root = await Deno.makeTempDir();
const storeDir = join(root, "store");
const keysDir = join(root, "keystore");
if (
Deno.env.has("XDG_CONFIG_HOME") == false &&
Deno.env.has("XDG_DATA_HOME") === false
) {
const root = await Deno.makeTempDir();
const storeDir = join(root, "data");
Deno.env.set("XDG_DATA_HOME", storeDir);
const config = join(root, "config");
Deno.env.set("XDG_CONFIG_HOME", config);
}

export function setNscData(p: string) {
Deno.env.set("XDG_DATA_HOME", p);
}

export function setNscConfig(p: string) {
Deno.env.set("XDG_CONFIG_HOME", p);
}

export function setNKeysDir(p: string) {
Deno.env.set("NKEYS_PATH", p);
}

export function getDataHome(): string {
return Deno.env.get("XDG_DATA_HOME") ?? "";
}

export function getConfigHome(): string {
return join(Deno.env.get("XDG_CONFIG_HOME") ?? "");
}

export function getKeysDir(): string {
if (Deno.env.has("NKEYS_PATH")) {
return Deno.env.get("NKEYS_PATH")!;
}
return join(Deno.env.get("XDG_DATA_HOME") ?? "", "nats", "nsc", "keys");
}

Deno.env.set("NKEYS_PATH", keysDir);
await run("env", "--store", storeDir);
export function getStoresDir(): string {
const stores = join(
Deno.env.get("XDG_DATA_HOME") ?? "",
"nats",
"nsc",
"stores",
);
return stores;
}

export interface Std {
out: string;
err: string;
}

export interface Nsc {
env(): Promise<Std>;
addOperator(): Promise<[string, Std]>;
getOperator(n: string): Promise<string>;
getAccount(o: string, a: string): Promise<string>;
Expand All @@ -41,6 +84,9 @@ export interface Nsc {
}

export const nsc: Nsc = {
async env(): Promise<Std> {
return await run("env");
},
async addOperator(): Promise<[string, Std]> {
const name = nuid.next();
const std = await run("add", "operator", name);
Expand All @@ -56,7 +102,7 @@ export const nsc: Nsc = {
return Deno.readTextFile(userPath(o, a, u));
},
async findKeyPair(pk: string): Promise<KeyPair> {
const fp = join(keysDir, "keys", pk[0], pk.slice(1, 3), `${pk}.nk`);
const fp = join(getKeysDir(), "keys", pk[0], pk.slice(1, 3), `${pk}.nk`);
const seed = await Deno.readTextFile(fp);
return fromSeed(new TextEncoder().encode(seed));
},
Expand Down Expand Up @@ -126,31 +172,33 @@ export function parseTable(s: string): string[][] {
}

function operatorPath(n: string): string {
return join(storeDir, n, `${n}.jwt`);
return join(getStoresDir(), n, `${n}.jwt`);
}

function accountPath(o: string, a: string): string {
return join(storeDir, o, "accounts", a, `${a}.jwt`);
return join(getStoresDir(), o, "accounts", a, `${a}.jwt`);
}

function userPath(o: string, a: string, u: string): string {
return join(storeDir, o, "accounts", a, "users", `${u}.jwt`);
return join(getStoresDir(), o, "accounts", a, "users", `${u}.jwt`);
}

async function run(...args: string[]): Promise<Std> {
const cmd = [
Deno.env.get("CI") ? "/home/runner/work/jwt.js/jwt.js/nsc" : "nsc",
];
cmd.push(...args);
const nsc = Deno.run({
const opts: RunOptions = {
cmd: cmd,
stderr: "piped",
stdout: "piped",
stdin: "null",
env: {
NKEYS_PATH: keysDir,
XDG_DATA_HOME: getDataHome(),
XDG_CONFIG_HOME: getConfigHome(),
},
});
};
const nsc = Deno.run(opts);
const { success } = await nsc.status();
const out = new TextDecoder().decode(await nsc.output());
const err = new TextDecoder().decode(await nsc.stderrOutput());
Expand Down
100 changes: 100 additions & 0 deletions tests/nsc_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
getConfigHome,
getKeysDir,
getStoresDir,
nsc,
parseTable,
setNKeysDir,
setNscConfig,
setNscData,
} from "./nsc.ts";
import {
assertEquals,
assertFalse,
} from "https://deno.land/std/testing/asserts.ts";
import { join } from "https://deno.land/std@0.177.0/path/mod.ts";
import { assert } from "https://raw.githubusercontent.com/nats-io/nats.deno/v1.7.0-rc/nats-base-client/denobuffer.ts";

Deno.test("nsc - env", async () => {
const std = await nsc.env();
const table = parseTable(std.err);
const nscHome = table.find((v) => {
return v[0] === "$NSC_HOME";
})?.[2];
assertEquals(nscHome, join(getConfigHome(), "nats", "nsc"));

const nkeys = table.find((v) => {
return v[0] === "$NKEYS_PATH";
});
const nkeysPath = nkeys?.[2];
assertEquals(nkeysPath, getKeysDir());
const nkeysPathSet = nkeys?.[1] === "Yes" || false;
assertFalse(nkeysPathSet);

const storeDir = table.find((v) => {
return v[0] === "Default Stores Dir";
})?.[2];
assertEquals(storeDir, getStoresDir());
});

Deno.test("nsc - set env nkeys_path", async () => {
const dir = await Deno.makeTempDir({ prefix: "my_test_" });
setNscData(join(dir, "data"));
setNscConfig(join(dir, "config"));
const nkeysDir = join(dir, "this_are_my_keys");
setNKeysDir(nkeysDir);

const std = await nsc.env();
const table = parseTable(std.err);
const nscHome = table.find((v) => {
return v[0] === "$NSC_HOME";
})?.[2];
assertEquals(nscHome, join(getConfigHome(), "nats", "nsc"));
assert(nscHome?.includes("my_test_"));

const nkeys = table.find((v) => {
return v[0] === "$NKEYS_PATH";
});
const nkeysPath = nkeys?.[2];
assertEquals(nkeysPath, nkeysDir);
assert(nkeysPath?.includes("my_test_"));

const nkeysPathSet = nkeys?.[1] === "Yes" || false;
assert(nkeysPathSet);

const storeDir = table.find((v) => {
return v[0] === "Default Stores Dir";
})?.[2];
assertEquals(storeDir, getStoresDir());
assert(storeDir?.includes("my_test_"));
});

Deno.test("nsc - set nkeys dir", async () => {
const dir = await Deno.makeTempDir({ prefix: "my_test_" });
setNscData(join(dir, "data"));
setNscConfig(join(dir, "config"));

const std = await nsc.env();
const table = parseTable(std.err);
const nscHome = table.find((v) => {
return v[0] === "$NSC_HOME";
})?.[2];
assertEquals(nscHome, join(getConfigHome(), "nats", "nsc"));
assert(nscHome?.includes("my_test_"));

const nkeys = table.find((v) => {
return v[0] === "$NKEYS_PATH";
});
const nkeysPath = nkeys?.[2];
assertEquals(nkeysPath, getKeysDir());
assert(nkeysPath?.includes("my_test_"));

const nkeysPathSet = nkeys?.[1] === "Yes" || false;
assert(nkeysPathSet);

const storeDir = table.find((v) => {
return v[0] === "Default Stores Dir";
})?.[2];
assertEquals(storeDir, getStoresDir());
assert(storeDir?.includes("my_test_"));
});

0 comments on commit 77cf256

Please sign in to comment.