127 lines
5.3 KiB
Plaintext
127 lines
5.3 KiB
Plaintext
@using ZB.MOM.WW.OtOpcUa.Admin.Services
|
|
@using ZB.MOM.WW.OtOpcUa.Configuration.Entities
|
|
@using ZB.MOM.WW.OtOpcUa.Configuration.Enums
|
|
@inject NodeAclService AclSvc
|
|
|
|
<div class="d-flex justify-content-between mb-3">
|
|
<h4>Access-control grants</h4>
|
|
<button class="btn btn-sm btn-primary" @onclick="() => _showForm = true">Add grant</button>
|
|
</div>
|
|
|
|
@if (_acls is null) { <p>Loading…</p> }
|
|
else if (_acls.Count == 0) { <p class="text-muted">No ACL grants in this draft. Publish will result in a cluster with no external access.</p> }
|
|
else
|
|
{
|
|
<table class="table table-sm">
|
|
<thead><tr><th>LDAP group</th><th>Scope</th><th>Scope ID</th><th>Permissions</th><th></th></tr></thead>
|
|
<tbody>
|
|
@foreach (var a in _acls)
|
|
{
|
|
<tr>
|
|
<td>@a.LdapGroup</td>
|
|
<td>@a.ScopeKind</td>
|
|
<td><code>@(a.ScopeId ?? "-")</code></td>
|
|
<td><code>@a.PermissionFlags</code></td>
|
|
<td><button class="btn btn-sm btn-outline-danger" @onclick="() => RevokeAsync(a.NodeAclRowId)">Revoke</button></td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
}
|
|
|
|
@if (_showForm)
|
|
{
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">LDAP group</label>
|
|
<input class="form-control" @bind="_group"/>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Scope kind</label>
|
|
<select class="form-select" @bind="_scopeKind">
|
|
@foreach (var k in Enum.GetValues<NodeAclScopeKind>()) { <option value="@k">@k</option> }
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Scope ID (empty for Cluster-wide)</label>
|
|
<input class="form-control" @bind="_scopeId"/>
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label">Permissions (bundled presets — per-flag editor in v2.1)</label>
|
|
<select class="form-select" @bind="_preset">
|
|
<option value="Read">Read (Browse + Read)</option>
|
|
<option value="WriteOperate">Read + Write Operate</option>
|
|
<option value="Engineer">Read + Write Tune + Write Configure</option>
|
|
<option value="AlarmAck">Read + Alarm Ack</option>
|
|
<option value="Full">Full (every flag)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
@if (_error is not null) { <div class="alert alert-danger mt-3">@_error</div> }
|
|
<div class="mt-3">
|
|
<button class="btn btn-sm btn-primary" @onclick="SaveAsync">Save</button>
|
|
<button class="btn btn-sm btn-secondary ms-2" @onclick="() => _showForm = false">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@code {
|
|
[Parameter] public long GenerationId { get; set; }
|
|
[Parameter] public string ClusterId { get; set; } = string.Empty;
|
|
|
|
private List<NodeAcl>? _acls;
|
|
private bool _showForm;
|
|
private string _group = string.Empty;
|
|
private NodeAclScopeKind _scopeKind = NodeAclScopeKind.Cluster;
|
|
private string _scopeId = string.Empty;
|
|
private string _preset = "Read";
|
|
private string? _error;
|
|
|
|
protected override async Task OnParametersSetAsync() =>
|
|
_acls = await AclSvc.ListAsync(GenerationId, CancellationToken.None);
|
|
|
|
private NodePermissions ResolvePreset() => _preset switch
|
|
{
|
|
"Read" => NodePermissions.Browse | NodePermissions.Read,
|
|
"WriteOperate" => NodePermissions.Browse | NodePermissions.Read | NodePermissions.WriteOperate,
|
|
"Engineer" => NodePermissions.Browse | NodePermissions.Read | NodePermissions.WriteTune | NodePermissions.WriteConfigure,
|
|
"AlarmAck" => NodePermissions.Browse | NodePermissions.Read | NodePermissions.AlarmRead | NodePermissions.AlarmAcknowledge,
|
|
"Full" => unchecked((NodePermissions)(-1)),
|
|
_ => NodePermissions.Browse | NodePermissions.Read,
|
|
};
|
|
|
|
private async Task SaveAsync()
|
|
{
|
|
_error = null;
|
|
if (string.IsNullOrWhiteSpace(_group)) { _error = "LDAP group is required"; return; }
|
|
|
|
var scopeId = _scopeKind == NodeAclScopeKind.Cluster ? null
|
|
: string.IsNullOrWhiteSpace(_scopeId) ? null : _scopeId;
|
|
|
|
if (_scopeKind != NodeAclScopeKind.Cluster && scopeId is null)
|
|
{
|
|
_error = $"ScopeId required for {_scopeKind}";
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
await AclSvc.GrantAsync(GenerationId, ClusterId, _group, _scopeKind, scopeId,
|
|
ResolvePreset(), notes: null, CancellationToken.None);
|
|
_group = string.Empty; _scopeId = string.Empty;
|
|
_showForm = false;
|
|
_acls = await AclSvc.ListAsync(GenerationId, CancellationToken.None);
|
|
}
|
|
catch (Exception ex) { _error = ex.Message; }
|
|
}
|
|
|
|
private async Task RevokeAsync(Guid rowId)
|
|
{
|
|
await AclSvc.RevokeAsync(rowId, CancellationToken.None);
|
|
_acls = await AclSvc.ListAsync(GenerationId, CancellationToken.None);
|
|
}
|
|
}
|