Files
scadalink-design/src/ScadaLink.CLI/Commands/SecurityCommands.cs
Joseph Doherty c63fb1c4a6 feat: achieve CLI parity with Central UI
Add 33 new management message records, ManagementActor handlers, and CLI
commands to close all functionality gaps between the Central UI and the
Management CLI. New capabilities include:

- Template member CRUD (attributes, alarms, scripts, compositions)
- Shared script CRUD
- Database connection definition CRUD
- Inbound API method CRUD
- LDAP scope rule management
- API key enable/disable
- Area update
- Remote event log and parked message queries
- Missing get/update commands for templates, sites, instances, data
  connections, external systems, notifications, and SMTP config

Includes 12 new ManagementActor unit tests covering authorization,
happy-path queries, and error handling. Updates CLI README and component
design documents (Component-CLI.md, Component-ManagementService.md).
2026-03-18 01:21:20 -04:00

173 lines
7.9 KiB
C#

using System.CommandLine;
using System.CommandLine.Parsing;
using ScadaLink.Commons.Messages.Management;
namespace ScadaLink.CLI.Commands;
public static class SecurityCommands
{
public static Command Build(Option<string> contactPointsOption, Option<string> formatOption)
{
var command = new Command("security") { Description = "Manage security settings" };
command.Add(BuildApiKey(contactPointsOption, formatOption));
command.Add(BuildRoleMapping(contactPointsOption, formatOption));
command.Add(BuildScopeRule(contactPointsOption, formatOption));
return command;
}
private static Command BuildApiKey(Option<string> contactPointsOption, Option<string> formatOption)
{
var group = new Command("api-key") { Description = "Manage API keys" };
var listCmd = new Command("list") { Description = "List all API keys" };
listCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListApiKeysCommand());
});
group.Add(listCmd);
var nameOption = new Option<string>("--name") { Description = "API key name", Required = true };
var createCmd = new Command("create") { Description = "Create an API key" };
createCmd.Add(nameOption);
createCmd.SetAction(async (ParseResult result) =>
{
var name = result.GetValue(nameOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new CreateApiKeyCommand(name));
});
group.Add(createCmd);
var idOption = new Option<int>("--id") { Description = "API key ID", Required = true };
var deleteCmd = new Command("delete") { Description = "Delete an API key" };
deleteCmd.Add(idOption);
deleteCmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteApiKeyCommand(id));
});
group.Add(deleteCmd);
var updateIdOption = new Option<int>("--id") { Description = "API key ID", Required = true };
var enabledOption = new Option<bool>("--enabled") { Description = "Enable or disable", Required = true };
var updateCmd = new Command("update") { Description = "Enable or disable an API key" };
updateCmd.Add(updateIdOption);
updateCmd.Add(enabledOption);
updateCmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(updateIdOption);
var enabled = result.GetValue(enabledOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new UpdateApiKeyCommand(id, enabled));
});
group.Add(updateCmd);
return group;
}
private static Command BuildRoleMapping(Option<string> contactPointsOption, Option<string> formatOption)
{
var group = new Command("role-mapping") { Description = "Manage LDAP role mappings" };
var listCmd = new Command("list") { Description = "List all role mappings" };
listCmd.SetAction(async (ParseResult result) =>
{
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListRoleMappingsCommand());
});
group.Add(listCmd);
var ldapGroupOption = new Option<string>("--ldap-group") { Description = "LDAP group name", Required = true };
var roleOption = new Option<string>("--role") { Description = "Role name", Required = true };
var createCmd = new Command("create") { Description = "Create a role mapping" };
createCmd.Add(ldapGroupOption);
createCmd.Add(roleOption);
createCmd.SetAction(async (ParseResult result) =>
{
var ldapGroup = result.GetValue(ldapGroupOption)!;
var role = result.GetValue(roleOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
new CreateRoleMappingCommand(ldapGroup, role));
});
group.Add(createCmd);
var idOption = new Option<int>("--id") { Description = "Mapping ID", Required = true };
var deleteCmd = new Command("delete") { Description = "Delete a role mapping" };
deleteCmd.Add(idOption);
deleteCmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(idOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteRoleMappingCommand(id));
});
group.Add(deleteCmd);
var updateIdOption = new Option<int>("--id") { Description = "Mapping ID", Required = true };
var updateLdapGroupOption = new Option<string>("--ldap-group") { Description = "LDAP group name", Required = true };
var updateRoleOption = new Option<string>("--role") { Description = "Role name", Required = true };
var updateCmd = new Command("update") { Description = "Update a role mapping" };
updateCmd.Add(updateIdOption);
updateCmd.Add(updateLdapGroupOption);
updateCmd.Add(updateRoleOption);
updateCmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(updateIdOption);
var ldapGroup = result.GetValue(updateLdapGroupOption)!;
var role = result.GetValue(updateRoleOption)!;
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption,
new UpdateRoleMappingCommand(id, ldapGroup, role));
});
group.Add(updateCmd);
return group;
}
private static Command BuildScopeRule(Option<string> contactPointsOption, Option<string> formatOption)
{
var group = new Command("scope-rule") { Description = "Manage LDAP scope rules" };
var mappingIdOption = new Option<int>("--mapping-id") { Description = "Role mapping ID", Required = true };
var listCmd = new Command("list") { Description = "List scope rules for a mapping" };
listCmd.Add(mappingIdOption);
listCmd.SetAction(async (ParseResult result) =>
{
var mappingId = result.GetValue(mappingIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new ListScopeRulesCommand(mappingId));
});
group.Add(listCmd);
var addMappingIdOption = new Option<int>("--mapping-id") { Description = "Role mapping ID", Required = true };
var siteIdOption = new Option<int>("--site-id") { Description = "Site ID", Required = true };
var addCmd = new Command("add") { Description = "Add a scope rule" };
addCmd.Add(addMappingIdOption);
addCmd.Add(siteIdOption);
addCmd.SetAction(async (ParseResult result) =>
{
var mappingId = result.GetValue(addMappingIdOption);
var siteId = result.GetValue(siteIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new AddScopeRuleCommand(mappingId, siteId));
});
group.Add(addCmd);
var deleteIdOption = new Option<int>("--id") { Description = "Scope rule ID", Required = true };
var deleteCmd = new Command("delete") { Description = "Delete a scope rule" };
deleteCmd.Add(deleteIdOption);
deleteCmd.SetAction(async (ParseResult result) =>
{
var id = result.GetValue(deleteIdOption);
return await CommandHelpers.ExecuteCommandAsync(
result, contactPointsOption, formatOption, new DeleteScopeRuleCommand(id));
});
group.Add(deleteCmd);
return group;
}
}