cisco-ise 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EXAMPLES.md +46 -0
- package/RADIUS.md +19 -0
- package/README.md +222 -0
- package/bin/cisco-ise.js +14 -0
- package/cli/commands/auth-profile.js +36 -0
- package/cli/commands/config.js +140 -0
- package/cli/commands/deployment.js +49 -0
- package/cli/commands/endpoint.js +167 -0
- package/cli/commands/guest.js +220 -0
- package/cli/commands/identity-group.js +24 -0
- package/cli/commands/internal-user.js +162 -0
- package/cli/commands/network-device.js +167 -0
- package/cli/commands/radius.js +326 -0
- package/cli/commands/session.js +123 -0
- package/cli/commands/tacacs.js +125 -0
- package/cli/commands/trustsec.js +37 -0
- package/cli/formatters/csv.js +10 -0
- package/cli/formatters/json.js +5 -0
- package/cli/formatters/table.js +29 -0
- package/cli/formatters/toon.js +6 -0
- package/cli/index.js +44 -0
- package/cli/utils/api.js +297 -0
- package/cli/utils/audit.js +30 -0
- package/cli/utils/config.js +125 -0
- package/cli/utils/confirm.js +34 -0
- package/cli/utils/connection.js +47 -0
- package/cli/utils/failure-reasons.js +2086 -0
- package/cli/utils/mac.js +18 -0
- package/cli/utils/output.js +42 -0
- package/cli/utils/spinner.js +19 -0
- package/cli/utils/time.js +21 -0
- package/cli/utils/wordlist.js +9 -0
- package/docs/PHASES.md +38 -0
- package/package.json +45 -0
- package/skills/cisco-ise-cli/SKILL.md +346 -0
- package/test/cli/api.test.js +67 -0
- package/test/cli/audit.test.js +31 -0
- package/test/cli/config.test.js +60 -0
- package/test/cli/confirm.test.js +34 -0
- package/test/cli/connection.test.js +54 -0
- package/test/cli/formatters.test.js +41 -0
- package/test/cli/mac.test.js +37 -0
- package/test/cli/time.test.js +30 -0
- package/test/integration/ise.test.js +425 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const { parse } = require("csv-parse/sync");
|
|
3
|
+
const { resolveConnection } = require("../utils/connection.js");
|
|
4
|
+
const { printResult, printError, printDryRun } = require("../utils/output.js");
|
|
5
|
+
const { checkWriteAllowed } = require("../utils/confirm.js");
|
|
6
|
+
const { normalizeMac } = require("../utils/mac.js");
|
|
7
|
+
const audit = require("../utils/audit.js");
|
|
8
|
+
const IseClient = require("../utils/api.js");
|
|
9
|
+
|
|
10
|
+
async function resolveGroupId(client, groupName) {
|
|
11
|
+
const data = await client.ersGet("/endpointgroup", { size: 100 });
|
|
12
|
+
const groups = data?.SearchResult?.resources || [];
|
|
13
|
+
const match = groups.find((g) => g.name.toLowerCase() === groupName.toLowerCase());
|
|
14
|
+
if (!match) throw new Error(`Identity group "${groupName}" not found. Run: cisco-ise identity-group list`);
|
|
15
|
+
return match.id;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function findEndpointByMac(client, mac) {
|
|
19
|
+
const normalized = normalizeMac(mac);
|
|
20
|
+
const data = await client.ersGet(`/endpoint`, { filter: `mac.EQ.${normalized}`, size: 1 });
|
|
21
|
+
const resources = data?.SearchResult?.resources;
|
|
22
|
+
if (!resources?.length) throw new Error(`Endpoint with MAC ${normalized} not found.`);
|
|
23
|
+
const detail = await client.ersGet(`/endpoint/${resources[0].id}`);
|
|
24
|
+
return detail?.ERSEndPoint || detail;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = function (program) {
|
|
28
|
+
const cmd = program.command("endpoint").description("Manage ISE endpoints");
|
|
29
|
+
|
|
30
|
+
cmd.command("list")
|
|
31
|
+
.description("List all endpoints")
|
|
32
|
+
.option("--limit <n>", "max results", parseInt)
|
|
33
|
+
.option("--page <n>", "specific page", parseInt)
|
|
34
|
+
.option("--page-size <n>", "results per page", parseInt)
|
|
35
|
+
.action(async (opts, command) => {
|
|
36
|
+
try {
|
|
37
|
+
const globalOpts = command.optsWithGlobals();
|
|
38
|
+
const conn = resolveConnection(globalOpts);
|
|
39
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
40
|
+
|
|
41
|
+
const resources = await client.ersPaginateAll("/endpoint", {}, {
|
|
42
|
+
limit: opts.limit,
|
|
43
|
+
page: opts.page,
|
|
44
|
+
pageSize: opts.pageSize,
|
|
45
|
+
});
|
|
46
|
+
await printResult(resources, globalOpts.format);
|
|
47
|
+
} catch (err) { printError(err); }
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
cmd.command("search")
|
|
51
|
+
.description("Search endpoints")
|
|
52
|
+
.option("--mac <mac>", "search by MAC address")
|
|
53
|
+
.option("--group <name>", "filter by identity group name")
|
|
54
|
+
.action(async (opts, command) => {
|
|
55
|
+
try {
|
|
56
|
+
const globalOpts = command.optsWithGlobals();
|
|
57
|
+
const conn = resolveConnection(globalOpts);
|
|
58
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
59
|
+
|
|
60
|
+
const params = {};
|
|
61
|
+
const filters = [];
|
|
62
|
+
if (opts.mac) filters.push(`mac.EQ.${normalizeMac(opts.mac)}`);
|
|
63
|
+
if (opts.group) {
|
|
64
|
+
const groupId = await resolveGroupId(client, opts.group);
|
|
65
|
+
filters.push(`groupId.EQ.${groupId}`);
|
|
66
|
+
}
|
|
67
|
+
if (filters.length) params.filter = filters.join(",");
|
|
68
|
+
|
|
69
|
+
const resources = await client.ersPaginateAll("/endpoint", params);
|
|
70
|
+
await printResult(resources, globalOpts.format);
|
|
71
|
+
} catch (err) { printError(err); }
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
cmd.command("add")
|
|
75
|
+
.description("Add an endpoint")
|
|
76
|
+
.option("--mac <mac>", "MAC address")
|
|
77
|
+
.option("--group <name>", "identity group name")
|
|
78
|
+
.option("--description <desc>", "endpoint description")
|
|
79
|
+
.option("--csv <file>", "bulk import from CSV (columns: mac, group, description)")
|
|
80
|
+
.action(async (opts, command) => {
|
|
81
|
+
try {
|
|
82
|
+
const globalOpts = command.optsWithGlobals();
|
|
83
|
+
const conn = resolveConnection(globalOpts);
|
|
84
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
85
|
+
const client = new IseClient(conn, {
|
|
86
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (opts.csv) {
|
|
90
|
+
const content = fs.readFileSync(opts.csv, "utf8");
|
|
91
|
+
const records = parse(content, { columns: true, skip_empty_lines: true });
|
|
92
|
+
const results = [];
|
|
93
|
+
for (const record of records) {
|
|
94
|
+
const mac = normalizeMac(record.mac);
|
|
95
|
+
const body = { ERSEndPoint: { mac, description: record.description || "" } };
|
|
96
|
+
if (record.group) {
|
|
97
|
+
body.ERSEndPoint.groupId = await resolveGroupId(client, record.group);
|
|
98
|
+
}
|
|
99
|
+
const result = await client.ersPost("/endpoint", body);
|
|
100
|
+
if (result.dryRun) { printDryRun(result); }
|
|
101
|
+
else { results.push({ mac, status: "created" }); }
|
|
102
|
+
}
|
|
103
|
+
if (results.length) {
|
|
104
|
+
if (globalOpts.audit !== false) audit.log({ command: "endpoint add (csv)", count: results.length });
|
|
105
|
+
await printResult(results, globalOpts.format);
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!opts.mac) throw new Error("--mac is required (or use --csv for bulk import)");
|
|
111
|
+
const mac = normalizeMac(opts.mac);
|
|
112
|
+
const body = { ERSEndPoint: { mac, description: opts.description || "" } };
|
|
113
|
+
if (opts.group) {
|
|
114
|
+
body.ERSEndPoint.groupId = await resolveGroupId(client, opts.group);
|
|
115
|
+
}
|
|
116
|
+
const result = await client.ersPost("/endpoint", body);
|
|
117
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
118
|
+
if (globalOpts.audit !== false) audit.log({ command: "endpoint add", mac });
|
|
119
|
+
console.log(`Endpoint ${mac} created.`);
|
|
120
|
+
} catch (err) { printError(err); }
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
cmd.command("update <mac>")
|
|
124
|
+
.description("Update an endpoint")
|
|
125
|
+
.option("--group <name>", "new identity group")
|
|
126
|
+
.option("--description <desc>", "new description")
|
|
127
|
+
.action(async (mac, opts, command) => {
|
|
128
|
+
try {
|
|
129
|
+
const globalOpts = command.optsWithGlobals();
|
|
130
|
+
const conn = resolveConnection(globalOpts);
|
|
131
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
132
|
+
const client = new IseClient(conn, {
|
|
133
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const existing = await findEndpointByMac(client, mac);
|
|
137
|
+
const body = { ERSEndPoint: { ...existing } };
|
|
138
|
+
if (opts.description !== undefined) body.ERSEndPoint.description = opts.description;
|
|
139
|
+
if (opts.group) {
|
|
140
|
+
body.ERSEndPoint.groupId = await resolveGroupId(client, opts.group);
|
|
141
|
+
}
|
|
142
|
+
const result = await client.ersPut(`/endpoint/${existing.id}`, body);
|
|
143
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
144
|
+
if (globalOpts.audit !== false) audit.log({ command: "endpoint update", mac: normalizeMac(mac) });
|
|
145
|
+
console.log(`Endpoint ${normalizeMac(mac)} updated.`);
|
|
146
|
+
} catch (err) { printError(err); }
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
cmd.command("delete <mac>")
|
|
150
|
+
.description("Delete an endpoint")
|
|
151
|
+
.action(async (mac, opts, command) => {
|
|
152
|
+
try {
|
|
153
|
+
const globalOpts = command.optsWithGlobals();
|
|
154
|
+
const conn = resolveConnection(globalOpts);
|
|
155
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
156
|
+
const client = new IseClient(conn, {
|
|
157
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const existing = await findEndpointByMac(client, mac);
|
|
161
|
+
const result = await client.ersDelete(`/endpoint/${existing.id}`);
|
|
162
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
163
|
+
if (globalOpts.audit !== false) audit.log({ command: "endpoint delete", mac: normalizeMac(mac) });
|
|
164
|
+
console.log(`Endpoint ${normalizeMac(mac)} deleted.`);
|
|
165
|
+
} catch (err) { printError(err); }
|
|
166
|
+
});
|
|
167
|
+
};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const { parse } = require("csv-parse/sync");
|
|
3
|
+
const { resolveConnection } = require("../utils/connection.js");
|
|
4
|
+
const { printResult, printError, printDryRun } = require("../utils/output.js");
|
|
5
|
+
const { checkWriteAllowed } = require("../utils/confirm.js");
|
|
6
|
+
const { parseDuration } = require("../utils/time.js");
|
|
7
|
+
const audit = require("../utils/audit.js");
|
|
8
|
+
const IseClient = require("../utils/api.js");
|
|
9
|
+
|
|
10
|
+
async function resolveSponsorPortalId(client) {
|
|
11
|
+
const data = await client.ersGet("/sponsorportal", { size: 100 });
|
|
12
|
+
const portals = data?.SearchResult?.resources || [];
|
|
13
|
+
if (!portals.length) throw new Error("No sponsor portals found.");
|
|
14
|
+
return portals[0].id;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = function (program) {
|
|
18
|
+
const cmd = program.command("guest").description("Manage ISE guest users");
|
|
19
|
+
|
|
20
|
+
cmd.command("list")
|
|
21
|
+
.description("List guest users")
|
|
22
|
+
.option("--limit <n>", "max results", parseInt)
|
|
23
|
+
.action(async (opts, command) => {
|
|
24
|
+
try {
|
|
25
|
+
const globalOpts = command.optsWithGlobals();
|
|
26
|
+
const conn = resolveConnection(globalOpts);
|
|
27
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
28
|
+
|
|
29
|
+
const resources = await client.sponsorPaginateAll("/guestuser", {}, { limit: opts.limit });
|
|
30
|
+
await printResult(resources, globalOpts.format);
|
|
31
|
+
} catch (err) { printError(err); }
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
cmd.command("search")
|
|
35
|
+
.description("Search guest users")
|
|
36
|
+
.option("--name <name>", "search by name")
|
|
37
|
+
.option("--status <status>", "filter by status (ACTIVE, SUSPENDED, etc.)")
|
|
38
|
+
.action(async (opts, command) => {
|
|
39
|
+
try {
|
|
40
|
+
const globalOpts = command.optsWithGlobals();
|
|
41
|
+
const conn = resolveConnection(globalOpts);
|
|
42
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
43
|
+
|
|
44
|
+
const filters = [];
|
|
45
|
+
if (opts.name) filters.push(`name.CONTAINS.${opts.name}`);
|
|
46
|
+
if (opts.status) filters.push(`status.EQ.${opts.status}`);
|
|
47
|
+
const params = filters.length ? { filter: filters.join(",") } : {};
|
|
48
|
+
|
|
49
|
+
const resources = await client.sponsorPaginateAll("/guestuser", params);
|
|
50
|
+
await printResult(resources, globalOpts.format);
|
|
51
|
+
} catch (err) { printError(err); }
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
cmd.command("create")
|
|
55
|
+
.description("Create a guest user")
|
|
56
|
+
.option("--first <name>", "first name")
|
|
57
|
+
.option("--last <name>", "last name")
|
|
58
|
+
.option("--email <email>", "email address")
|
|
59
|
+
.option("--portal <name>", "guest portal name")
|
|
60
|
+
.option("--duration <duration>", "account duration (e.g., 1d, 8h)")
|
|
61
|
+
.option("--csv <file>", "bulk create from CSV (columns: first, last, email, portal, duration)")
|
|
62
|
+
.action(async (opts, command) => {
|
|
63
|
+
try {
|
|
64
|
+
const globalOpts = command.optsWithGlobals();
|
|
65
|
+
const conn = resolveConnection(globalOpts);
|
|
66
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
67
|
+
const client = new IseClient(conn, {
|
|
68
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (opts.csv) {
|
|
72
|
+
const content = fs.readFileSync(opts.csv, "utf8");
|
|
73
|
+
const records = parse(content, { columns: true, skip_empty_lines: true });
|
|
74
|
+
const results = [];
|
|
75
|
+
for (const record of records) {
|
|
76
|
+
const portalId = await resolvePortalId(client, record.portal || "Sponsored Guest Portal (default)");
|
|
77
|
+
const body = {
|
|
78
|
+
GuestUser: {
|
|
79
|
+
guestType: "Contractor (default)",
|
|
80
|
+
portalId,
|
|
81
|
+
guestInfo: {
|
|
82
|
+
firstName: record.first,
|
|
83
|
+
lastName: record.last,
|
|
84
|
+
emailAddress: record.email,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
const result = await client.sponsorPost("/guestuser", body);
|
|
89
|
+
if (result.dryRun) { printDryRun(result); }
|
|
90
|
+
else { results.push({ name: `${record.first} ${record.last}`, status: "created" }); }
|
|
91
|
+
}
|
|
92
|
+
if (results.length) {
|
|
93
|
+
if (globalOpts.audit !== false) audit.log({ command: "guest create (csv)", count: results.length });
|
|
94
|
+
await printResult(results, globalOpts.format);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!opts.first || !opts.last) throw new Error("--first and --last are required");
|
|
100
|
+
const portalId = await resolveSponsorPortalId(client);
|
|
101
|
+
const now = new Date();
|
|
102
|
+
const end = new Date(now.getTime() + (opts.duration ? parseDuration(opts.duration) : 24 * 60 * 60 * 1000));
|
|
103
|
+
const fmt = (d) => `${d.getMonth()+1}/${d.getDate()}/${d.getFullYear()} ${String(d.getHours()).padStart(2,"0")}:${String(d.getMinutes()).padStart(2,"0")}`;
|
|
104
|
+
const body = {
|
|
105
|
+
GuestUser: {
|
|
106
|
+
guestType: "Contractor (default)",
|
|
107
|
+
portalId,
|
|
108
|
+
guestInfo: {
|
|
109
|
+
userName: `${opts.first.toLowerCase()}_${opts.last.toLowerCase()}`,
|
|
110
|
+
firstName: opts.first,
|
|
111
|
+
lastName: opts.last,
|
|
112
|
+
emailAddress: opts.email || "",
|
|
113
|
+
password: "G" + Math.random().toString(36).slice(2, 6) + Math.floor(Math.random() * 900 + 100) + "!Aa",
|
|
114
|
+
},
|
|
115
|
+
guestAccessInfo: {
|
|
116
|
+
validDays: 1,
|
|
117
|
+
fromDate: fmt(now),
|
|
118
|
+
toDate: fmt(end),
|
|
119
|
+
location: "San Jose",
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
const result = await client.sponsorPost("/guestuser", body);
|
|
124
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
125
|
+
if (globalOpts.audit !== false) audit.log({ command: "guest create", name: `${opts.first} ${opts.last}` });
|
|
126
|
+
console.log(`Guest user "${opts.first} ${opts.last}" created (username: ${body.GuestUser.guestInfo.userName}, password: ${body.GuestUser.guestInfo.password}).`);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (err.response?.data) process.stderr.write(JSON.stringify(err.response.data, null, 2) + "\n");
|
|
129
|
+
printError(err);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
cmd.command("extend <id>")
|
|
134
|
+
.description("Extend guest account duration")
|
|
135
|
+
.option("--duration <duration>", "extension duration (e.g., 1d, 8h)", "1d")
|
|
136
|
+
.action(async (id, opts, command) => {
|
|
137
|
+
try {
|
|
138
|
+
const globalOpts = command.optsWithGlobals();
|
|
139
|
+
const conn = resolveConnection(globalOpts);
|
|
140
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
141
|
+
const client = new IseClient(conn, {
|
|
142
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const ms = parseDuration(opts.duration);
|
|
146
|
+
const hours = Math.ceil(ms / (60 * 60 * 1000));
|
|
147
|
+
const result = await client.sponsorPut(`/guestuser/resetpassword/${id}`, {});
|
|
148
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
149
|
+
if (globalOpts.audit !== false) audit.log({ command: "guest extend", id, duration: opts.duration });
|
|
150
|
+
console.log(`Guest user ${id} extended by ${hours} hours.`);
|
|
151
|
+
} catch (err) { printError(err); }
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
cmd.command("suspend <id>")
|
|
155
|
+
.description("Suspend a guest user")
|
|
156
|
+
.action(async (id, opts, command) => {
|
|
157
|
+
try {
|
|
158
|
+
const globalOpts = command.optsWithGlobals();
|
|
159
|
+
const conn = resolveConnection(globalOpts);
|
|
160
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
161
|
+
const client = new IseClient(conn, {
|
|
162
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const result = await client.sponsorPut(`/guestuser/suspend/name/${id}`, {});
|
|
166
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
167
|
+
if (globalOpts.audit !== false) audit.log({ command: "guest suspend", id });
|
|
168
|
+
console.log(`Guest user ${id} suspended.`);
|
|
169
|
+
} catch (err) { printError(err); }
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
cmd.command("reinstate <id>")
|
|
173
|
+
.description("Reinstate a suspended guest user")
|
|
174
|
+
.action(async (id, opts, command) => {
|
|
175
|
+
try {
|
|
176
|
+
const globalOpts = command.optsWithGlobals();
|
|
177
|
+
const conn = resolveConnection(globalOpts);
|
|
178
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
179
|
+
const client = new IseClient(conn, {
|
|
180
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const result = await client.sponsorPut(`/guestuser/reinstate/name/${id}`, {});
|
|
184
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
185
|
+
if (globalOpts.audit !== false) audit.log({ command: "guest reinstate", id });
|
|
186
|
+
console.log(`Guest user ${id} reinstated.`);
|
|
187
|
+
} catch (err) { printError(err); }
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
cmd.command("delete <id>")
|
|
191
|
+
.description("Delete a guest user")
|
|
192
|
+
.action(async (id, opts, command) => {
|
|
193
|
+
try {
|
|
194
|
+
const globalOpts = command.optsWithGlobals();
|
|
195
|
+
const conn = resolveConnection(globalOpts);
|
|
196
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
197
|
+
const client = new IseClient(conn, {
|
|
198
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const result = await client.sponsorDelete(`/guestuser/${id}`);
|
|
202
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
203
|
+
if (globalOpts.audit !== false) audit.log({ command: "guest delete", id });
|
|
204
|
+
console.log(`Guest user ${id} deleted.`);
|
|
205
|
+
} catch (err) { printError(err); }
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
cmd.command("portals")
|
|
209
|
+
.description("List available guest portals")
|
|
210
|
+
.action(async (opts, command) => {
|
|
211
|
+
try {
|
|
212
|
+
const globalOpts = command.optsWithGlobals();
|
|
213
|
+
const conn = resolveConnection(globalOpts);
|
|
214
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
215
|
+
|
|
216
|
+
const resources = await client.ersPaginateAll("/portal");
|
|
217
|
+
await printResult(resources, globalOpts.format);
|
|
218
|
+
} catch (err) { printError(err); }
|
|
219
|
+
});
|
|
220
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { resolveConnection } = require("../utils/connection.js");
|
|
2
|
+
const { printResult, printError } = require("../utils/output.js");
|
|
3
|
+
const IseClient = require("../utils/api.js");
|
|
4
|
+
|
|
5
|
+
module.exports = function (program) {
|
|
6
|
+
const cmd = program.command("identity-group").description("List ISE identity groups");
|
|
7
|
+
|
|
8
|
+
cmd.command("list")
|
|
9
|
+
.description("List identity groups")
|
|
10
|
+
.option("--type <type>", "filter by type (endpoint, user)")
|
|
11
|
+
.action(async (opts, command) => {
|
|
12
|
+
try {
|
|
13
|
+
const globalOpts = command.optsWithGlobals();
|
|
14
|
+
const conn = resolveConnection(globalOpts);
|
|
15
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
16
|
+
|
|
17
|
+
let endpoint = "/endpointgroup";
|
|
18
|
+
if (opts.type === "user") endpoint = "/identitygroup";
|
|
19
|
+
|
|
20
|
+
const resources = await client.ersPaginateAll(endpoint);
|
|
21
|
+
await printResult(resources, globalOpts.format);
|
|
22
|
+
} catch (err) { printError(err); }
|
|
23
|
+
});
|
|
24
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const { resolveConnection } = require("../utils/connection.js");
|
|
2
|
+
const { printResult, printError, printDryRun } = require("../utils/output.js");
|
|
3
|
+
const { checkWriteAllowed } = require("../utils/confirm.js");
|
|
4
|
+
const audit = require("../utils/audit.js");
|
|
5
|
+
const IseClient = require("../utils/api.js");
|
|
6
|
+
|
|
7
|
+
async function resolveGroupId(client, groupName) {
|
|
8
|
+
const data = await client.ersGet("/identitygroup", { size: 100 });
|
|
9
|
+
const groups = data?.SearchResult?.resources || [];
|
|
10
|
+
const match = groups.find((g) => g.name.toLowerCase() === groupName.toLowerCase());
|
|
11
|
+
if (!match) throw new Error(`Identity group "${groupName}" not found. Run: cisco-ise identity-group list --type user`);
|
|
12
|
+
return match.id;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function findUserByName(client, name) {
|
|
16
|
+
const data = await client.ersGet("/internaluser", { filter: `name.EQ.${name}`, size: 1 });
|
|
17
|
+
const resources = data?.SearchResult?.resources;
|
|
18
|
+
if (!resources?.length) throw new Error(`Internal user "${name}" not found.`);
|
|
19
|
+
const detail = await client.ersGet(`/internaluser/${resources[0].id}`);
|
|
20
|
+
return detail?.InternalUser || detail;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = function (program) {
|
|
24
|
+
const cmd = program.command("internal-user").description("Manage ISE internal users");
|
|
25
|
+
|
|
26
|
+
cmd.command("list")
|
|
27
|
+
.description("List internal users")
|
|
28
|
+
.option("--limit <n>", "max results", parseInt)
|
|
29
|
+
.action(async (opts, command) => {
|
|
30
|
+
try {
|
|
31
|
+
const globalOpts = command.optsWithGlobals();
|
|
32
|
+
const conn = resolveConnection(globalOpts);
|
|
33
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
34
|
+
|
|
35
|
+
const resources = await client.ersPaginateAll("/internaluser", {}, { limit: opts.limit });
|
|
36
|
+
await printResult(resources, globalOpts.format);
|
|
37
|
+
} catch (err) { printError(err); }
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
cmd.command("get <name>")
|
|
41
|
+
.description("Get internal user details")
|
|
42
|
+
.action(async (name, opts, command) => {
|
|
43
|
+
try {
|
|
44
|
+
const globalOpts = command.optsWithGlobals();
|
|
45
|
+
const conn = resolveConnection(globalOpts);
|
|
46
|
+
const client = new IseClient(conn, { noCache: !globalOpts.cache, debug: globalOpts.debug });
|
|
47
|
+
|
|
48
|
+
const user = await findUserByName(client, name);
|
|
49
|
+
const { link, ...clean } = user;
|
|
50
|
+
await printResult(clean, globalOpts.format);
|
|
51
|
+
} catch (err) { printError(err); }
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
cmd.command("add")
|
|
55
|
+
.description("Create an internal user")
|
|
56
|
+
.option("--user-name <name>", "username")
|
|
57
|
+
.option("--user-password <pass>", "user password")
|
|
58
|
+
.option("--email <email>", "email address")
|
|
59
|
+
.option("--first <name>", "first name")
|
|
60
|
+
.option("--last <name>", "last name")
|
|
61
|
+
.option("--group <name>", "identity group name")
|
|
62
|
+
.option("--description <desc>", "user description")
|
|
63
|
+
.action(async (opts, command) => {
|
|
64
|
+
try {
|
|
65
|
+
const globalOpts = command.optsWithGlobals();
|
|
66
|
+
const conn = resolveConnection(globalOpts);
|
|
67
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
68
|
+
const client = new IseClient(conn, {
|
|
69
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!opts.userName) throw new Error("--user-name is required");
|
|
73
|
+
if (!opts.userPassword) throw new Error("--user-password is required");
|
|
74
|
+
|
|
75
|
+
const body = {
|
|
76
|
+
InternalUser: {
|
|
77
|
+
name: opts.userName,
|
|
78
|
+
password: opts.userPassword,
|
|
79
|
+
enabled: true,
|
|
80
|
+
changePassword: false,
|
|
81
|
+
description: opts.description || "",
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
if (opts.email) body.InternalUser.email = opts.email;
|
|
85
|
+
if (opts.first) body.InternalUser.firstName = opts.first;
|
|
86
|
+
if (opts.last) body.InternalUser.lastName = opts.last;
|
|
87
|
+
if (opts.group) {
|
|
88
|
+
body.InternalUser.identityGroups = await resolveGroupId(client, opts.group);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const result = await client.ersPost("/internaluser", body);
|
|
92
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
93
|
+
if (globalOpts.audit !== false) audit.log({ command: "internal-user add", name: opts.userName });
|
|
94
|
+
console.log(`Internal user "${opts.userName}" created.`);
|
|
95
|
+
} catch (err) { printError(err); }
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
cmd.command("update <name>")
|
|
99
|
+
.description("Update an internal user")
|
|
100
|
+
.option("--user-password <pass>", "new password")
|
|
101
|
+
.option("--email <email>", "email address")
|
|
102
|
+
.option("--first <name>", "first name")
|
|
103
|
+
.option("--last <name>", "last name")
|
|
104
|
+
.option("--group <name>", "identity group name")
|
|
105
|
+
.option("--description <desc>", "description")
|
|
106
|
+
.option("--enable", "enable the user")
|
|
107
|
+
.option("--disable", "disable the user")
|
|
108
|
+
.action(async (name, opts, command) => {
|
|
109
|
+
try {
|
|
110
|
+
const globalOpts = command.optsWithGlobals();
|
|
111
|
+
const conn = resolveConnection(globalOpts);
|
|
112
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
113
|
+
const client = new IseClient(conn, {
|
|
114
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const existing = await findUserByName(client, name);
|
|
118
|
+
const body = {
|
|
119
|
+
InternalUser: {
|
|
120
|
+
id: existing.id,
|
|
121
|
+
name: existing.name,
|
|
122
|
+
enabled: existing.enabled,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (opts.userPassword) body.InternalUser.password = opts.userPassword;
|
|
127
|
+
if (opts.email) body.InternalUser.email = opts.email;
|
|
128
|
+
if (opts.first) body.InternalUser.firstName = opts.first;
|
|
129
|
+
if (opts.last) body.InternalUser.lastName = opts.last;
|
|
130
|
+
if (opts.description !== undefined) body.InternalUser.description = opts.description;
|
|
131
|
+
if (opts.enable) body.InternalUser.enabled = true;
|
|
132
|
+
if (opts.disable) body.InternalUser.enabled = false;
|
|
133
|
+
if (opts.group) {
|
|
134
|
+
body.InternalUser.identityGroups = await resolveGroupId(client, opts.group);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const result = await client.ersPut(`/internaluser/${existing.id}`, body);
|
|
138
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
139
|
+
if (globalOpts.audit !== false) audit.log({ command: "internal-user update", name });
|
|
140
|
+
console.log(`Internal user "${name}" updated.`);
|
|
141
|
+
} catch (err) { printError(err); }
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
cmd.command("delete <name>")
|
|
145
|
+
.description("Delete an internal user")
|
|
146
|
+
.action(async (name, opts, command) => {
|
|
147
|
+
try {
|
|
148
|
+
const globalOpts = command.optsWithGlobals();
|
|
149
|
+
const conn = resolveConnection(globalOpts);
|
|
150
|
+
await checkWriteAllowed(conn, globalOpts);
|
|
151
|
+
const client = new IseClient(conn, {
|
|
152
|
+
noCache: !globalOpts.cache, debug: globalOpts.debug, dryRun: globalOpts.dryRun,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const existing = await findUserByName(client, name);
|
|
156
|
+
const result = await client.ersDelete(`/internaluser/${existing.id}`);
|
|
157
|
+
if (result.dryRun) { printDryRun(result); return; }
|
|
158
|
+
if (globalOpts.audit !== false) audit.log({ command: "internal-user delete", name });
|
|
159
|
+
console.log(`Internal user "${name}" deleted.`);
|
|
160
|
+
} catch (err) { printError(err); }
|
|
161
|
+
});
|
|
162
|
+
};
|