feature: Adds user command.
This commit is contained in:
parent
2f7f2088cd
commit
ee6556adb6
7 changed files with 157 additions and 6 deletions
|
@ -8,6 +8,7 @@ import type {
|
|||
SessionToken,
|
||||
RouteOptions,
|
||||
} from "./libs/types.js";
|
||||
|
||||
import type { BackendBaseClass } from "./backendimpl/base.js";
|
||||
|
||||
import { route as getPermissions } from "./routes/getPermissions.js";
|
||||
|
|
|
@ -60,6 +60,7 @@ export function route(routeOptions: RouteOptions) {
|
|||
return {
|
||||
success: true,
|
||||
data: users.map(i => ({
|
||||
id: i.id,
|
||||
name: i.name,
|
||||
email: i.email,
|
||||
isServiceAccount: i.isRootServiceAccount,
|
||||
|
|
|
@ -5,12 +5,14 @@ import { run as backends } from "./commands/backends.js";
|
|||
import { run as users } from "./commands/users.js";
|
||||
|
||||
export type PrintLine = (...str: any[]) => void;
|
||||
export type KeyboardRead = (disableEcho?: boolean) => Promise<string>;
|
||||
|
||||
type Command = (
|
||||
args: string[],
|
||||
println: PrintLine,
|
||||
axios: Axios,
|
||||
apiKey: string,
|
||||
keyboardRead: KeyboardRead
|
||||
) => Promise<void>;
|
||||
|
||||
type Commands = {
|
||||
|
|
|
@ -88,4 +88,5 @@ export async function run(
|
|||
program.addCommand(lookupBackend);
|
||||
|
||||
program.parse(argv);
|
||||
await new Promise((resolve) => program.onExit(resolve));
|
||||
}
|
||||
|
|
|
@ -1,17 +1,29 @@
|
|||
import type { Axios } from "axios";
|
||||
|
||||
import { SSHCommand } from "../libs/patchCommander.js";
|
||||
import type { PrintLine } from "../commands.js";
|
||||
import type { PrintLine, KeyboardRead } from "../commands.js";
|
||||
|
||||
type UserLookupSuccess = {
|
||||
success: true;
|
||||
data: {
|
||||
id: number,
|
||||
isServiceAccount: boolean,
|
||||
username: string,
|
||||
name: string,
|
||||
email: string
|
||||
}[];
|
||||
};
|
||||
|
||||
export async function run(
|
||||
argv: string[],
|
||||
println: PrintLine,
|
||||
axios: Axios,
|
||||
apiKey: string,
|
||||
readKeyboard: KeyboardRead
|
||||
) {
|
||||
const program = new SSHCommand(println);
|
||||
program.description("Manages users for NextNet");
|
||||
program.version("v0.1.0-preprod");
|
||||
program.version("v1.0.0-testing");
|
||||
|
||||
const addCommand = new SSHCommand(println, "add");
|
||||
addCommand.description("Create a new user");
|
||||
|
@ -25,21 +37,155 @@ export async function run(
|
|||
"Asks for a password. Hides output",
|
||||
);
|
||||
|
||||
addCommand.action(async(username: string, email: string, name: string, options: {
|
||||
password?: string,
|
||||
askPassword?: boolean
|
||||
}) => {
|
||||
if (!options.password && !options.askPassword) {
|
||||
println("No password supplied, and askpass has not been supplied.\n");
|
||||
return;
|
||||
};
|
||||
|
||||
let password: string = "";
|
||||
|
||||
if (options.askPassword) {
|
||||
let passwordConfirmOne = "a";
|
||||
let passwordConfirmTwo = "b";
|
||||
|
||||
while (passwordConfirmOne != passwordConfirmTwo) {
|
||||
println("Password: ");
|
||||
passwordConfirmOne = await readKeyboard(true);
|
||||
|
||||
println("\nConfirm password: ");
|
||||
passwordConfirmTwo = await readKeyboard(true);
|
||||
|
||||
println("\n");
|
||||
|
||||
if (passwordConfirmOne != passwordConfirmTwo) {
|
||||
println("Passwords do not match! Try again.\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
password = passwordConfirmOne;
|
||||
} else {
|
||||
// From the first check we do, we know this is safe (you MUST specify a password)
|
||||
// @ts-ignore
|
||||
password = options.password;
|
||||
}
|
||||
|
||||
const response = await axios.post("/api/v1/users/create", {
|
||||
name,
|
||||
username,
|
||||
email,
|
||||
password
|
||||
});
|
||||
|
||||
if (response.status != 200) {
|
||||
if (process.env.NODE_ENV != "production") console.log(response);
|
||||
|
||||
if (response.data.error) {
|
||||
println(`Error: ${response.data.error}\n`);
|
||||
} else {
|
||||
println("Error requesting connections!\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
println("User created successfully.\n");
|
||||
})
|
||||
|
||||
const removeCommand = new SSHCommand(println, "rm");
|
||||
removeCommand.description("Remove a user");
|
||||
removeCommand.argument("<uid>", "ID of user to remove");
|
||||
|
||||
removeCommand.action(async(uidStr: string) => {
|
||||
const uid = parseInt(uidStr);
|
||||
|
||||
if (Number.isNaN(uid)) {
|
||||
println("UID (%s) is not a number.\n", uid);
|
||||
return;
|
||||
}
|
||||
|
||||
let response = await axios.post("/api/v1/users/remove", {
|
||||
token: apiKey,
|
||||
uid
|
||||
});
|
||||
|
||||
if (response.status != 200) {
|
||||
if (process.env.NODE_ENV != "production") console.log(response);
|
||||
|
||||
if (response.data.error) {
|
||||
println(`Error: ${response.data.error}\n`);
|
||||
} else {
|
||||
println("Error deleting user!\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
println("User has been successfully deleted.\n");
|
||||
});
|
||||
|
||||
const lookupCommand = new SSHCommand(println, "find");
|
||||
lookupCommand.description("Find a user");
|
||||
lookupCommand.option("-i, --id <id>", "UID of User");
|
||||
lookupCommand.option("-n, --name <name>", "Name of User");
|
||||
lookupCommand.option("-u, --username <username>", "Username of User");
|
||||
lookupCommand.option("-e, --email <email>", "Email of User");
|
||||
lookupCommand.option("-s, --service", "The User is a service account");
|
||||
lookupCommand.option("-s, --service", "The user is a service account");
|
||||
|
||||
lookupCommand.action(async(options) => {
|
||||
// FIXME: redundant parseInt calls
|
||||
|
||||
if (options.id) {
|
||||
const uid = parseInt(options.id);
|
||||
|
||||
if (Number.isNaN(uid)) {
|
||||
println("UID (%s) is not a number.\n", uid);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
const response = await axios.post("/api/v1/users/lookup", {
|
||||
token: apiKey,
|
||||
id: options.id ? parseInt(options.id) : undefined,
|
||||
name: options.name,
|
||||
username: options.username,
|
||||
email: options.email,
|
||||
service: Boolean(options.service)
|
||||
});
|
||||
|
||||
if (response.status != 200) {
|
||||
if (process.env.NODE_ENV != "production") console.log(response);
|
||||
|
||||
if (response.data.error) {
|
||||
println(`Error: ${response.data.error}\n`);
|
||||
} else {
|
||||
println("Error finding user!\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { data }: UserLookupSuccess = response.data;
|
||||
|
||||
data.forEach((user) => {
|
||||
println("UID: %s%s:\n", user.id, (user.isServiceAccount ? " (service)" : ""));
|
||||
println("- Username: %s\n", user.username);
|
||||
println("- Name: %s\n", user.name);
|
||||
println("- Email: %s\n", user.email);
|
||||
|
||||
println("\n");
|
||||
});
|
||||
|
||||
println("%s users found.\n", response.data.data.length);
|
||||
})
|
||||
|
||||
program.addCommand(addCommand);
|
||||
program.addCommand(removeCommand);
|
||||
program.addCommand(lookupCommand);
|
||||
|
||||
program.parse(argv);
|
||||
await new Promise((resolve) => program.onExit(resolve));
|
||||
}
|
||||
|
|
|
@ -108,8 +108,8 @@ server.on("connection", client => {
|
|||
continue;
|
||||
}
|
||||
|
||||
await command.run(argv, println, axios, token);
|
||||
stream.write(`~$ `);
|
||||
await command.run(argv, println, axios, token, (disableEcho) => readFromKeyboard(stream, disableEcho));
|
||||
stream.write("~$ ");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ export class SSHCommand extends Command {
|
|||
this.configureOutput({
|
||||
writeOut: str => println(str),
|
||||
writeErr: str => {
|
||||
if (str.includes("--help") || str.includes("-h")) return;
|
||||
if (this.hasRecievedExitSignal) return;
|
||||
println(str);
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue