Files
scadalink-design/src/ScadaLink.CentralUI/Components/Layout/NavMenu.razor

165 lines
9.2 KiB
Plaintext

@using ScadaLink.Security
<nav class="sidebar d-flex flex-column">
<div class="brand">ScadaLink</div>
<div style="overflow-y:auto; flex:1 1 auto; min-height:0;">
<ul class="nav flex-column">
<li class="nav-item">
<NavLink class="nav-link" href="/" Match="NavLinkMatch.All">Dashboard</NavLink>
</li>
<AuthorizeView>
<Authorized>
@* Admin section — Admin role only *@
<AuthorizeView Policy="@AuthorizationPolicies.RequireAdmin">
<Authorized Context="adminContext">
<div role="presentation" class="nav-section-header">Admin</div>
<li class="nav-item">
<NavLink class="nav-link" href="/admin/ldap-mappings">LDAP Mappings</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/admin/sites">Sites</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/admin/api-keys">API Keys</NavLink>
</li>
</Authorized>
</AuthorizeView>
@* Design section — Design role *@
<AuthorizeView Policy="@AuthorizationPolicies.RequireDesign">
<Authorized Context="designContext">
<div role="presentation" class="nav-section-header">Design</div>
<li class="nav-item">
<NavLink class="nav-link" href="/design/templates">Templates</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/design/shared-scripts">Shared Scripts</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/design/connections">Connections</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/design/external-systems">External Systems</NavLink>
</li>
</Authorized>
</AuthorizeView>
@* Deployment section — Deployment role *@
<AuthorizeView Policy="@AuthorizationPolicies.RequireDeployment">
<Authorized Context="deploymentContext">
<div role="presentation" class="nav-section-header">Deployment</div>
<li class="nav-item">
<NavLink class="nav-link" href="/deployment/topology">Topology</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/deployment/deployments">Deployments</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/deployment/debug-view">Debug View</NavLink>
</li>
</Authorized>
</AuthorizeView>
@* Notifications — mixed-role section; each item gated by its own policy.
The header is ungated: every authenticated user holds at least one of
Admin/Design/Deployment, so it always has a visible child. *@
<div role="presentation" class="nav-section-header">Notifications</div>
<AuthorizeView Policy="@AuthorizationPolicies.RequireAdmin">
<Authorized Context="notifAdminContext">
<li class="nav-item">
<NavLink class="nav-link" href="/notifications/smtp">SMTP Configuration</NavLink>
</li>
</Authorized>
</AuthorizeView>
<AuthorizeView Policy="@AuthorizationPolicies.RequireDesign">
<Authorized Context="notifDesignContext">
<li class="nav-item">
<NavLink class="nav-link" href="/notifications/lists">Notification Lists</NavLink>
</li>
</Authorized>
</AuthorizeView>
<AuthorizeView Policy="@AuthorizationPolicies.RequireDeployment">
<Authorized Context="notifDeploymentContext">
<li class="nav-item">
<NavLink class="nav-link" href="/notifications/report">Notification Report</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/notifications/kpis">Notification KPIs</NavLink>
</li>
</Authorized>
</AuthorizeView>
@* Site Calls — Site Call Audit (#22). Deployment-role only,
matching the Notification Report page's gate; the section
header sits inside the policy block so a non-Deployment
user does not see the heading. *@
<AuthorizeView Policy="@AuthorizationPolicies.RequireDeployment">
<Authorized Context="siteCallsContext">
<div role="presentation" class="nav-section-header">Site Calls</div>
<li class="nav-item">
<NavLink class="nav-link" href="/site-calls/report">Site Calls</NavLink>
</li>
</Authorized>
</AuthorizeView>
@* Monitoring — Health Dashboard is all-roles; Event Logs and
Parked Messages are Deployment-role only (Component-CentralUI). *@
<div role="presentation" class="nav-section-header">Monitoring</div>
<li class="nav-item">
<NavLink class="nav-link" href="/monitoring/health">Health Dashboard</NavLink>
</li>
<AuthorizeView Policy="@AuthorizationPolicies.RequireDeployment">
<Authorized Context="monitoringContext">
<li class="nav-item">
<NavLink class="nav-link" href="/monitoring/event-logs">Event Logs</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/monitoring/parked-messages">Parked Messages</NavLink>
</li>
</Authorized>
</AuthorizeView>
@* Audit — gated on the OperationalAudit policy (#23 M7-T15
/ Bundle G). Hosts the new Audit Log page (#23 M7) and
the renamed Configuration Audit Log (IAuditService
config-change viewer). Both items share the same gate,
so the section header sits inside the same policy block:
a non-audit user does not even see the heading.
OperationalAudit is satisfied by the Admin, Audit, and
AuditReadOnly roles. *@
<AuthorizeView Policy="@AuthorizationPolicies.OperationalAudit">
<Authorized Context="auditContext">
<div role="presentation" class="nav-section-header">Audit</div>
<li class="nav-item">
<NavLink class="nav-link" href="/audit/log">Audit Log</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/audit/configuration">Configuration Audit Log</NavLink>
</li>
</Authorized>
</AuthorizeView>
</Authorized>
</AuthorizeView>
</ul>
</div>
<AuthorizeView>
<Authorized>
<div class="border-top border-secondary px-3 py-2">
<div class="d-flex justify-content-between align-items-center">
@* CentralUI-024: claim type resolved via JwtTokenService. *@
<span class="text-light small">@context.User.GetDisplayName()</span>
<form method="post" action="/auth/logout" data-enhance="false">
@* CentralUI-017: logout is a state-changing POST and is
CSRF-protected — the antiforgery token is required. *@
<AntiforgeryToken />
<button type="submit" class="btn btn-outline-light btn-sm py-0 px-2">Sign Out</button>
</form>
</div>
</div>
</Authorized>
</AuthorizeView>
</nav>