312 lines
8.1 KiB
C#
312 lines
8.1 KiB
C#
using Shadow.Data;
|
|
using Shadow.Entities;
|
|
|
|
namespace Shadow.Controllers;
|
|
public class Cli
|
|
{
|
|
private readonly ApplicationDbContext db;
|
|
private readonly GeneralUseHelpers guhf;
|
|
private readonly string[] args;
|
|
|
|
// TODO: Add "changeUser"
|
|
|
|
public Cli(ApplicationDbContext _db, GeneralUseHelpers _guhf, string[] _args)
|
|
{
|
|
db = _db;
|
|
guhf = _guhf;
|
|
args = _args;
|
|
}
|
|
|
|
public async Task<bool> Parse()
|
|
{
|
|
// Returns true if the program can finish execution.
|
|
bool exit = true;
|
|
// Check if anything has been passed
|
|
if (args.Length == 0 || args.FirstOrDefault() is null) {
|
|
return true;
|
|
}
|
|
switch (args[0].ToLower())
|
|
{
|
|
case "adduser":
|
|
AddUser();
|
|
break;
|
|
case "resetpassword":
|
|
ResetPassword();
|
|
break;
|
|
case "removeuser":
|
|
RemoveUser();
|
|
break;
|
|
case "help":
|
|
case "--help":
|
|
case "h":
|
|
case "-h":
|
|
case "/h":
|
|
case "/?":
|
|
case "?":
|
|
ShowHelp();
|
|
break;
|
|
default:
|
|
Console.WriteLine($"Unknown option: \"{args[0]}\". See \"help\" for available arguments.");
|
|
break;
|
|
}
|
|
|
|
return exit;
|
|
}
|
|
|
|
public void ShowHelp()
|
|
{
|
|
// Shown when "Shadow --help"/"Shadow help"/... is ran.
|
|
Console.WriteLine(
|
|
$"--- Shadow commandline utility ---\n" +
|
|
$"Shadow version: #{ThisAssembly.Git.Commit} {ThisAssembly.Git.Branch} ({ThisAssembly.Git.CommitDate})\n\n" +
|
|
$"Available commands:\n" +
|
|
$"- addUser [username] - create a new user,\n" +
|
|
$"- resetPassword [username] - reset a user's password,\n" +
|
|
$"- removeUser [username] - remove the user COMPLETELY.\n\n" +
|
|
$"Username is optional. If not provided, user will be prompted to enter it.\n" +
|
|
$"Running without specifying a command launches a Kestrel web server.\n\n" +
|
|
$"License: AGPLv3+, Source code: https://gitea.7o7.cx/sherl/Shadow"
|
|
);
|
|
}
|
|
|
|
public void AddUser()
|
|
{
|
|
// Check if any cli arguments have been passed
|
|
// Both:
|
|
// Shadow addUser
|
|
// and
|
|
// Shadow addUser username
|
|
// are supported.
|
|
|
|
Console.WriteLine($"[Shadow] Add a new user");
|
|
if (args.Length == 2)
|
|
Console.WriteLine($" You will be promped to enter a password.");
|
|
else
|
|
Console.WriteLine($" You will be promped to enter a username and password.");
|
|
|
|
string? username = null;
|
|
if (args.Length != 2)
|
|
{
|
|
while (username is null || username == String.Empty)
|
|
username = ReadName(" Please enter a username: ");
|
|
}
|
|
else
|
|
username = args[1];
|
|
|
|
// Check if user by this name exists in DB
|
|
User? foundUser = db.Users
|
|
.FirstOrDefault(u => u.NormalizedName == username!.ToLower());
|
|
if (foundUser != null) {
|
|
Console.WriteLine("Error! User with this name already exists in the database!");
|
|
return;
|
|
}
|
|
|
|
string password = ReadPassword();
|
|
string passwordConfirmation = ReadPassword(" Confirm password: ");
|
|
if (!password.Equals(passwordConfirmation))
|
|
{
|
|
Console.WriteLine("Error! Passwords do not match. Please try again.");
|
|
return;
|
|
}
|
|
bool isUserAdmin = YesNoPrompt($" Should \"{username}\" be an administrator? [y/N]: ", false);
|
|
|
|
User newUser = new User
|
|
{
|
|
Name = username!,
|
|
NormalizedName = username!.ToLower(),
|
|
Password = password
|
|
};
|
|
|
|
db.Users.Add(newUser);
|
|
db.SaveChanges();
|
|
}
|
|
|
|
public void ResetPassword()
|
|
{
|
|
// Check if any cli arguments have been passed
|
|
// Both:
|
|
// Shadow resetPassword
|
|
// and
|
|
// Shadow resetPassword username
|
|
// are supported.
|
|
|
|
Console.WriteLine($"[Shadow] Reset password");
|
|
if (args.Length == 2)
|
|
Console.WriteLine($" You will be promped to enter a new password.");
|
|
else
|
|
Console.WriteLine($" You will be promped to enter a username and a new password.");
|
|
|
|
string? username = null;
|
|
if (args.Length != 2)
|
|
{
|
|
while (username is null || username == String.Empty)
|
|
username = ReadName(" Please enter a username: ");
|
|
}
|
|
else
|
|
username = args[1];
|
|
|
|
// Check if user by this name exists in DB
|
|
User? foundUser = db.Users
|
|
.FirstOrDefault(u => u.NormalizedName == username!.ToLower());
|
|
if (foundUser == null)
|
|
{
|
|
Console.WriteLine("Error! User with this name does not exist in the database!");
|
|
return;
|
|
}
|
|
|
|
string password = ReadPassword();
|
|
string passwordConfirmation = ReadPassword(" Confirm new password: ");
|
|
if (!password.Equals(passwordConfirmation))
|
|
{
|
|
Console.WriteLine("Error! Passwords do not match. Please try again.");
|
|
return;
|
|
}
|
|
|
|
foundUser.Password = password;
|
|
db.SaveChanges();
|
|
|
|
}
|
|
|
|
public void RemoveUser()
|
|
{
|
|
// Check if any cli arguments have been passed
|
|
// Both:
|
|
// Shadow removeUser
|
|
// and
|
|
// Shadow removeUser username
|
|
// are supported.
|
|
|
|
Console.WriteLine($"[Shadow] Remove user");
|
|
if (args.Length == 2)
|
|
Console.WriteLine($" You will be promped to enter the password.");
|
|
else
|
|
Console.WriteLine($" You will be promped to enter the username and password.");
|
|
|
|
string? username = null;
|
|
if (args.Length != 2)
|
|
{
|
|
while (username is null || username == String.Empty)
|
|
username = ReadName(" Please enter the username: ");
|
|
}
|
|
else
|
|
username = args[1];
|
|
|
|
// Check if user by this name exists in DB
|
|
User? foundUser = db.Users
|
|
.FirstOrDefault(u => u.NormalizedName == username!.ToLower());
|
|
if (foundUser == null)
|
|
{
|
|
Console.WriteLine("Error! User with this name does not exist in the database!");
|
|
return;
|
|
}
|
|
|
|
string password = ReadPassword();
|
|
string passwordConfirmation = ReadPassword(" Confirm password: ");
|
|
if (!password.Equals(passwordConfirmation))
|
|
{
|
|
Console.WriteLine("Error! Passwords do not match. Please try again.");
|
|
return;
|
|
}
|
|
if (foundUser.Password != password)
|
|
{
|
|
Console.WriteLine($"Error! Entered password does not match that of \"{username}\"!");
|
|
return;
|
|
}
|
|
|
|
bool userDeletionConfirmation = YesNoPrompt($" Do you want to remove \"{username}\" completely?\n" +
|
|
$" This cannot be undone! [y/N]: ", false);
|
|
if (userDeletionConfirmation) {
|
|
|
|
// All playlists, interactions should be deleted as well.
|
|
List<AlbumInteraction> albumInteractions = db.AlbumInteractions
|
|
.Where(ai => ai.User == foundUser)
|
|
.ToList();
|
|
|
|
List<SongInteraction> songInteractions = db.SongInteractions
|
|
.Where(si => si.User == foundUser)
|
|
.ToList();
|
|
|
|
foreach(AlbumInteraction ai in albumInteractions)
|
|
db.AlbumInteractions.Remove(ai);
|
|
|
|
foreach (SongInteraction si in songInteractions)
|
|
db.SongInteractions.Remove(si);
|
|
|
|
db.Users.Remove(foundUser);
|
|
db.SaveChanges();
|
|
|
|
Console.WriteLine($"User \"{username}\" and all their data deleted successfully.");
|
|
}
|
|
else
|
|
Console.WriteLine("User not removed.");
|
|
|
|
}
|
|
|
|
public string ReadPassword(string prompt = " Enter password (will not be echoed back): ")
|
|
{
|
|
// https://www.silicloud.com/blog/how-to-hide-content-in-the-console-using-c/
|
|
string password = String.Empty;
|
|
ConsoleKeyInfo key;
|
|
|
|
bool exit = false;
|
|
Console.Write(prompt);
|
|
while (!exit) {
|
|
key = Console.ReadKey(true);
|
|
|
|
// Exit on enter
|
|
if (key.Key == ConsoleKey.Enter)
|
|
exit = true;
|
|
// Clear last character on backspace
|
|
else if (key.Key == ConsoleKey.Backspace && password.Length > 0)
|
|
password = password.Substring(0, (password.Length - 1));
|
|
// Append any other character
|
|
else
|
|
password += key.KeyChar;
|
|
}
|
|
Console.WriteLine();
|
|
return password;
|
|
}
|
|
|
|
public string? ReadName(string prompt = " Enter username: ")
|
|
{
|
|
Console.Write(prompt);
|
|
string? input = Console.ReadLine();
|
|
return input;
|
|
}
|
|
|
|
public bool YesNoPrompt(string prompt, bool? default_value = null)
|
|
{
|
|
// Checks if user input starts with "y", and if it does, returns true.
|
|
// Otherwise checks for "n". If both checks fail, and default_value is null,
|
|
// user will be asked repeatedly.
|
|
bool exit = false;
|
|
bool response = false;
|
|
|
|
Console.Write(prompt);
|
|
string? input = Console.ReadLine();
|
|
|
|
while (!exit)
|
|
{
|
|
if (input is not null && input.Length > 0)
|
|
{
|
|
if (input.ToLower().StartsWith("y"))
|
|
{
|
|
response = true;
|
|
exit = true;
|
|
}
|
|
else if (input.ToLower().StartsWith("n"))
|
|
{
|
|
response = false;
|
|
exit = true;
|
|
}
|
|
}
|
|
else if (default_value is not null)
|
|
{
|
|
response = (bool)default_value;
|
|
exit = true;
|
|
}
|
|
}
|
|
return response;
|
|
}
|
|
}
|