feat(cli): extend permission filter to resource-permissions command
This commit is contained in:
77
src/cli.js
77
src/cli.js
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import { parseArgs } from "node:util";
|
import { parseArgs } from "node:util";
|
||||||
import jmespath from "jmespath";
|
import jmespath from "jmespath";
|
||||||
|
import { minimatch } from "minimatch";
|
||||||
|
|
||||||
import { loadPublicConfig } from "./index.js";
|
import { loadPublicConfig } from "./index.js";
|
||||||
import { getGraphClient } from "./graph/auth.js";
|
import { getGraphClient } from "./graph/auth.js";
|
||||||
@@ -12,6 +13,7 @@ import {
|
|||||||
listAppPermissions,
|
listAppPermissions,
|
||||||
listAppPermissionsResolved,
|
listAppPermissionsResolved,
|
||||||
listAppGrants,
|
listAppGrants,
|
||||||
|
listResourcePermissions,
|
||||||
} from "./graph/app.js";
|
} from "./graph/app.js";
|
||||||
import { toMarkdownTable } from "./markdown.js";
|
import { toMarkdownTable } from "./markdown.js";
|
||||||
|
|
||||||
@@ -20,20 +22,20 @@ function usage() {
|
|||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
list-apps [--display-name|-n <name>]
|
list-apps [--display-name|-n <name>]
|
||||||
list-app-permissions --app-id|-i <appId> [--resolve|-r] [--short|-s]
|
list-app-permissions --app-id|-i <appId> [--resolve|-r] [--short|-s] [--filter|-f <glob>]
|
||||||
list-app-grants --app-id|-i <appId>
|
list-app-grants --app-id|-i <appId>
|
||||||
table [--pretty|-p] [--quote-guids|-g] [--header|-H <spec|auto|a>]
|
list-resource-permissions [--app-id|-i <appId> | --display-name|-n <name>]
|
||||||
|
table [--header|-H <spec|auto|a>]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-n, --display-name <name> Filter apps by exact display name
|
-n, --display-name <name> Filter apps by exact display name
|
||||||
-i, --app-id <appId> Application (client) ID
|
-i, --app-id <appId> Application (client) ID
|
||||||
-r, --resolve Resolve permission GUIDs to human-readable values
|
-r, --resolve Resolve permission GUIDs to human-readable values
|
||||||
-s, --short Makes output more compact
|
-s, --short Makes output more compact
|
||||||
|
-f, --filter <glob> Filter by permission name glob
|
||||||
-q, --query <jmespath> Filter output JSON using JMESPath
|
-q, --query <jmespath> Filter output JSON using JMESPath
|
||||||
-p, --pretty Use normalized column widths for Markdown table output
|
|
||||||
-g, --quote-guids In pretty tables, wrap GUID values in backticks
|
|
||||||
-H, --header <value> Header mode/spec: auto|a OR "col1, col2" OR "key1: Label 1, key2: Label 2"
|
-H, --header <value> Header mode/spec: auto|a OR "col1, col2" OR "key1: Label 1, key2: Label 2"
|
||||||
-o, --output <format> Output format: json|table|prettytable (default: json)
|
-o, --output <format> Output format: json|j|table|t|alignedtable|at|prettytable|pt (default: json)
|
||||||
-h, --help Show this help message`;
|
-h, --help Show this help message`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +79,15 @@ function parseHeaderSpec(headerValue) {
|
|||||||
return { mode: "map", map };
|
return { mode: "map", map };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeOutputFormat(outputValue) {
|
||||||
|
const raw = (outputValue ?? "json").toLowerCase();
|
||||||
|
if (raw === "json" || raw === "j") return "json";
|
||||||
|
if (raw === "table" || raw === "t") return "table";
|
||||||
|
if (raw === "alignedtable" || raw === "at") return "alignedtable";
|
||||||
|
if (raw === "prettytable" || raw === "pt") return "prettytable";
|
||||||
|
throw new Error("--output must be one of: json|j, table|t, alignedtable|at, prettytable|pt");
|
||||||
|
}
|
||||||
|
|
||||||
function omitPermissionGuidColumns(value) {
|
function omitPermissionGuidColumns(value) {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return value.map((item) => omitPermissionGuidColumns(item));
|
return value.map((item) => omitPermissionGuidColumns(item));
|
||||||
@@ -129,9 +140,8 @@ async function main() {
|
|||||||
"app-id": { type: "string", short: "i" },
|
"app-id": { type: "string", short: "i" },
|
||||||
resolve: { type: "boolean", short: "r" },
|
resolve: { type: "boolean", short: "r" },
|
||||||
short: { type: "boolean", short: "s" },
|
short: { type: "boolean", short: "s" },
|
||||||
|
filter: { type: "string", short: "f" },
|
||||||
query: { type: "string", short: "q" },
|
query: { type: "string", short: "q" },
|
||||||
pretty: { type: "boolean", short: "p" },
|
|
||||||
"quote-guids": { type: "boolean", short: "g" },
|
|
||||||
header: { type: "string", short: "H" },
|
header: { type: "string", short: "H" },
|
||||||
output: { type: "string", short: "o" },
|
output: { type: "string", short: "o" },
|
||||||
},
|
},
|
||||||
@@ -143,10 +153,7 @@ async function main() {
|
|||||||
console.log(usage());
|
console.log(usage());
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
const outputFormat = values.output ?? "json";
|
const outputFormat = normalizeOutputFormat(values.output);
|
||||||
if (outputFormat !== "json" && outputFormat !== "table" && outputFormat !== "prettytable") {
|
|
||||||
throw new Error("--output must be one of: json, table, prettytable");
|
|
||||||
}
|
|
||||||
|
|
||||||
let result;
|
let result;
|
||||||
switch (command) {
|
switch (command) {
|
||||||
@@ -175,9 +182,16 @@ async function main() {
|
|||||||
tenantId: config.tenantId,
|
tenantId: config.tenantId,
|
||||||
clientId: config.clientId,
|
clientId: config.clientId,
|
||||||
});
|
});
|
||||||
result = values.resolve
|
result = values.resolve || values.filter
|
||||||
? await listAppPermissionsResolved(client, values["app-id"])
|
? await listAppPermissionsResolved(client, values["app-id"])
|
||||||
: await listAppPermissions(client, values["app-id"]);
|
: await listAppPermissions(client, values["app-id"]);
|
||||||
|
if (values.filter) {
|
||||||
|
const pattern = values.filter;
|
||||||
|
result = result.filter((item) =>
|
||||||
|
minimatch(item.permissionValue ?? "", pattern, { nocase: true })
|
||||||
|
|| minimatch(item.permissionDisplayName ?? "", pattern, { nocase: true })
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "list-app-grants":
|
case "list-app-grants":
|
||||||
@@ -193,6 +207,32 @@ async function main() {
|
|||||||
result = await listAppGrants(client, values["app-id"]);
|
result = await listAppGrants(client, values["app-id"]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "list-resource-permissions":
|
||||||
|
if (!values["app-id"] && !values["display-name"]) {
|
||||||
|
throw new Error("--app-id or --display-name is required for list-resource-permissions");
|
||||||
|
}
|
||||||
|
if (values["app-id"] && values["display-name"]) {
|
||||||
|
throw new Error("Use either --app-id or --display-name for list-resource-permissions, not both");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const config = await loadPublicConfig();
|
||||||
|
const { client } = await getGraphClient({
|
||||||
|
tenantId: config.tenantId,
|
||||||
|
clientId: config.clientId,
|
||||||
|
});
|
||||||
|
result = await listResourcePermissions(client, {
|
||||||
|
appId: values["app-id"],
|
||||||
|
displayName: values["display-name"],
|
||||||
|
});
|
||||||
|
if (values.filter) {
|
||||||
|
const pattern = values.filter;
|
||||||
|
result = result.filter((item) =>
|
||||||
|
minimatch(item.permissionValue ?? "", pattern, { nocase: true })
|
||||||
|
|| minimatch(item.permissionDisplayName ?? "", pattern, { nocase: true })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown command: ${command}`);
|
throw new Error(`Unknown command: ${command}`);
|
||||||
}
|
}
|
||||||
@@ -205,17 +245,14 @@ async function main() {
|
|||||||
if (command === "table") {
|
if (command === "table") {
|
||||||
console.log(toMarkdownTable(
|
console.log(toMarkdownTable(
|
||||||
output,
|
output,
|
||||||
Boolean(values.pretty),
|
outputFormat === "alignedtable" || outputFormat === "prettytable",
|
||||||
Boolean(values["quote-guids"]),
|
outputFormat === "prettytable",
|
||||||
headerSpec,
|
headerSpec,
|
||||||
));
|
));
|
||||||
|
} else if (outputFormat === "alignedtable") {
|
||||||
|
console.log(toMarkdownTable(output, true, false, headerSpec));
|
||||||
} else if (outputFormat === "prettytable") {
|
} else if (outputFormat === "prettytable") {
|
||||||
console.log(toMarkdownTable(
|
console.log(toMarkdownTable(output, true, true, headerSpec));
|
||||||
output,
|
|
||||||
true,
|
|
||||||
Boolean(values["quote-guids"]),
|
|
||||||
headerSpec,
|
|
||||||
));
|
|
||||||
} else if (outputFormat === "table") {
|
} else if (outputFormat === "table") {
|
||||||
console.log(toMarkdownTable(output, false, false, headerSpec));
|
console.log(toMarkdownTable(output, false, false, headerSpec));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user