Werkzeugkasten: Zugangsdaten je Einheit (#692)
A unit holds credentials for its members, by tool and setting - needed once,
not once per agent, and other units never get them.
- UnitConfig.credentials, protected by ConfigSecrets like every secret.
- OrgChart.ToolSettingsFor: an agent's own settings, completed by the nearest
unit up that holds the value; own non-empty values win; no tool is granted.
Used for runs, tool jobs and preflight probes.
- OrgAdministration.SetUnitCredential, PUT /organisation/units/{id}/credentials;
the view lists held settings, never values.
- Templates never carry unit credentials; they are named instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
661807b2d0
commit
9a265888f8
@@ -271,6 +271,44 @@ public sealed partial class OrgAdministration(AppHost host, ILogger logger)
|
||||
return AdminResult.Success(trimmed.Count == 0 ? message : message + $" Lowered with it: {string.Join(", ", trimmed)}.");
|
||||
}
|
||||
|
||||
// ─── credentials per unit (#692) ───
|
||||
|
||||
/// <summary>
|
||||
/// #692: Sets or removes one credential a unit holds for its members' tool. The value is
|
||||
/// written protected and never shown again — the audit names the setting, not the value.
|
||||
/// </summary>
|
||||
public AdminResult SetUnitCredential(string unitId, string tool, string key, string? value, string reason = "")
|
||||
{
|
||||
if (FindUnit(unitId) is not { } unit)
|
||||
return AdminResult.Failure($"There is no unit '{unitId}'.");
|
||||
tool = (tool ?? "").Trim();
|
||||
key = (key ?? "").Trim();
|
||||
if (tool.Length == 0 || key.Length == 0)
|
||||
return AdminResult.Failure("Name the tool and the setting the credential is for.");
|
||||
|
||||
unit.Credentials ??= new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
||||
var toolKey = unit.Credentials.Keys.FirstOrDefault(k => Same(k, tool)) ?? tool;
|
||||
if (!unit.Credentials.TryGetValue(toolKey, out var held))
|
||||
unit.Credentials[toolKey] = held = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var remove = string.IsNullOrEmpty(value);
|
||||
if (remove)
|
||||
{
|
||||
held.Remove(held.Keys.FirstOrDefault(k => Same(k, key)) ?? key);
|
||||
if (held.Count == 0)
|
||||
unit.Credentials.Remove(toolKey);
|
||||
if (unit.Credentials.Count == 0)
|
||||
unit.Credentials = null;
|
||||
}
|
||||
else
|
||||
held[held.Keys.FirstOrDefault(k => Same(k, key)) ?? key] = value!;
|
||||
|
||||
Save(null, "unit_credential", new { unitId, tool = toolKey, key, set = !remove }, reason);
|
||||
return AdminResult.Success(remove
|
||||
? $"'{unit.Name}' no longer holds {toolKey}.{key}."
|
||||
: $"'{unit.Name}' holds {toolKey}.{key} for its members and the units below — where an agent has {toolKey} and no value of its own.");
|
||||
}
|
||||
|
||||
// ─── votes (#702) ───
|
||||
|
||||
/// <summary>
|
||||
@@ -1004,7 +1042,7 @@ public sealed partial class OrgAdministration(AppHost host, ILogger logger)
|
||||
{
|
||||
Id = u.Id, Name = u.Name, ParentId = u.ParentId, LeaderAgentId = u.LeaderAgentId,
|
||||
Goal = u.Goal, ToolCeiling = u.ToolCeiling?.ToList(), Defaults = u.Defaults, Approvals = u.Approvals,
|
||||
Inspection = u.Inspection, Votes = u.Votes, TemplateOrigin = u.TemplateOrigin
|
||||
Inspection = u.Inspection, Votes = u.Votes, TemplateOrigin = u.TemplateOrigin, Credentials = u.Credentials
|
||||
};
|
||||
|
||||
private static bool Same(string? a, string? b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
Reference in New Issue
Block a user