diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/Templates.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/Templates.razor index a8d3e0e0..8c9c4591 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/Templates.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/Templates.razor @@ -130,10 +130,16 @@ private bool _loading = true; private string? _errorMessage; + // "New Instance…" leaves the Design area for a RequireDeployment page; only offer + // it to users who hold that role so the menu never leads to a 403. + private bool _canCreateInstances; + private ToastNotification _toast = default!; protected override async Task OnInitializedAsync() { + var state = await AuthStateProvider.GetAuthenticationStateAsync(); + _canCreateInstances = state.User.HasClaim(JwtTokenService.RoleClaimType, Roles.Deployer); await LoadTemplatesAsync(); } @@ -302,6 +308,13 @@ { } + @if (_canCreateInstances) + { + + + + } @if (tmpl != null) diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TemplatesPageTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TemplatesPageTests.cs index 255af811..45494b64 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TemplatesPageTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/TemplatesPageTests.cs @@ -47,15 +47,16 @@ public class TemplatesPageTests : BunitContext JSInterop.SetupVoid("treeviewStorage.save", _ => true); } - private void AddTestAuth() + private void AddTestAuth(params string[] roles) { // The page resolves the current user via the "Username" claim in // GetCurrentUserAsync(); supply a stub so OnInitializedAsync doesn't crash. - var claims = new[] - { - new Claim(JwtTokenService.UsernameClaimType, "tester"), - new Claim(JwtTokenService.RoleClaimType, "Designer") - }; + // Calling this again from a test replaces the constructor's Designer-only + // default (MS DI resolves the LAST registration of a service), which is how + // a test renders the page as a Deployer. + if (roles.Length == 0) roles = new[] { Roles.Designer }; + var claims = new List { new(JwtTokenService.UsernameClaimType, "tester") }; + claims.AddRange(roles.Select(r => new Claim(JwtTokenService.RoleClaimType, r))); var identity = new ClaimsIdentity(claims, "TestAuth"); var user = new ClaimsPrincipal(identity); Services.AddSingleton(new TestAuthStateProvider(user)); @@ -396,6 +397,57 @@ public class TemplatesPageTests : BunitContext Assert.EndsWith("/design/templates/create", nav.Uri); } + // --------------------------------------------------------------- + // "New Instance…" context-menu item (Deployer-gated) + // --------------------------------------------------------------- + + private void SeedOneTemplate(int id, string name) + { + _repo.GetAllTemplatesAsync(Arg.Any()) + .Returns(Task.FromResult>(new List