diff --git a/backend/api/Program.cs b/backend/api/Program.cs index 8dc423ca8..b166eb394 100644 --- a/backend/api/Program.cs +++ b/backend/api/Program.cs @@ -19,6 +19,8 @@ Console.WriteLine($"\nENVIRONMENT IS SET TO '{builder.Environment.EnvironmentName}'\n"); +builder.Configuration.AddEnvironmentVariables(); + builder.AddAzureEnvironmentVariables(); if (builder.Configuration.GetSection("KeyVault").GetValue("UseKeyVault")) diff --git a/backend/api/Services/AccessRoleService.cs b/backend/api/Services/AccessRoleService.cs index 1193fd4e1..a3c05b7a6 100644 --- a/backend/api/Services/AccessRoleService.cs +++ b/backend/api/Services/AccessRoleService.cs @@ -10,7 +10,6 @@ public interface IAccessRoleService public Task> GetAllowedInstallationCodes(List roles); public bool IsUserAdmin(); public bool IsAuthenticationAvailable(); - public string? GetRequestNameId(); } public class AccessRoleService(FlotillaDbContext context, IHttpContextAccessor httpContextAccessor) : IAccessRoleService @@ -54,10 +53,5 @@ public bool IsAuthenticationAvailable() { return httpContextAccessor.HttpContext != null; } - - public string? GetRequestNameId() - { - return httpContextAccessor.HttpContext?.GetUserNameId(); - } } } diff --git a/backend/api/Services/SignalRService.cs b/backend/api/Services/SignalRService.cs index 873dc2fac..6ea49221f 100644 --- a/backend/api/Services/SignalRService.cs +++ b/backend/api/Services/SignalRService.cs @@ -11,7 +11,7 @@ public interface ISignalRService public Task SendMessageAsync(string label, Installation? installation, string message); } - public class SignalRService(IHubContext signalRHub, IAccessRoleService accessRoleService) : ISignalRService + public class SignalRService(IHubContext signalRHub, IConfiguration configuration) : ISignalRService { private readonly JsonSerializerOptions _serializerOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; @@ -23,25 +23,24 @@ public async Task SendMessageAsync(string label, Installation? installation, public async Task SendMessageAsync(string label, Installation? installation, string message) { - if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development") + if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Local") { - string? nameId = accessRoleService.GetRequestNameId(); - if (nameId is null) return; + string? localDevUser = configuration.GetSection("Local")["DevUserId"]; + if (localDevUser is null || localDevUser.Equals("", StringComparison.Ordinal)) return; + if (installation != null) - await signalRHub.Clients.Group(nameId + installation.InstallationCode.ToUpper(CultureInfo.CurrentCulture)).SendAsync(label, "all", message); + await signalRHub.Clients.Group(localDevUser + installation.InstallationCode.ToUpper(CultureInfo.CurrentCulture)).SendAsync(label, "all", message); else - // TODO: can't do this if we use DEV. Then we instead need a generic group for connection id - await signalRHub.Clients.User(nameId).SendAsync(label, "all", message); + await signalRHub.Clients.Group(localDevUser).SendAsync(label, "all", message); } else { if (installation != null) await signalRHub.Clients.Group(installation.InstallationCode.ToUpper(CultureInfo.CurrentCulture)).SendAsync(label, "all", message); else - // TODO: can't do this if we use DEV. Then we instead need a generic group for connection id await signalRHub.Clients.All.SendAsync(label, "all", message); } - + await Task.CompletedTask; } diff --git a/backend/api/SignalR/SignalRHub.cs b/backend/api/SignalR/SignalRHub.cs index d91f6ba63..ad89dce84 100644 --- a/backend/api/SignalR/SignalRHub.cs +++ b/backend/api/SignalR/SignalRHub.cs @@ -7,7 +7,7 @@ public interface ISignalRClient { } - public class SignalRHub(IAccessRoleService accessRoleService) : Hub + public class SignalRHub(IAccessRoleService accessRoleService, IConfiguration configuration) : Hub { /// /// Called when a new connection is made. @@ -18,14 +18,18 @@ public override async Task OnConnectedAsync() { var roles = Context.User.Claims .Where((c) => c.Type.EndsWith("/role", StringComparison.CurrentCulture)).Select((c) => c.Value).ToList(); - + var installationCodes = await accessRoleService.GetAllowedInstallationCodes(roles); - if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development") + if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Local") { - string? objectId = Context.User.Claims.Where((c) => c.Type.EndsWith("/objectidentifier", StringComparison.CurrentCulture)).Select((c) => c.Value).FirstOrDefault(); + string? localDevUser = configuration.GetSection("Local")["DevUserId"]; + if (localDevUser is null || localDevUser.Equals("", StringComparison.Ordinal)) + throw new HubException("Running in development mode, but missing Local__DevUserId value in environment"); + + await Groups.AddToGroupAsync(Context.ConnectionId, localDevUser); // This is used instead of Users.All foreach (string installationCode in installationCodes) - await Groups.AddToGroupAsync(Context.ConnectionId, objectId + installationCode.ToUpperInvariant()); + await Groups.AddToGroupAsync(Context.ConnectionId, localDevUser + installationCode.ToUpperInvariant()); } else { diff --git a/backend/api/Utilities/HttpContextExtensions.cs b/backend/api/Utilities/HttpContextExtensions.cs index 88a8047f1..223dcbf1e 100644 --- a/backend/api/Utilities/HttpContextExtensions.cs +++ b/backend/api/Utilities/HttpContextExtensions.cs @@ -14,19 +14,6 @@ public static string GetRequestToken(this HttpContext client) return value.ToString().Replace("Bearer ", "", StringComparison.CurrentCulture); } - public static string? GetUserNameId(this HttpContext client) - { - string accessTokenBase64 = client.GetRequestToken(); - - var handler = new JwtSecurityTokenHandler(); - var jwtSecurityToken = handler.ReadJwtToken(accessTokenBase64); - - var claims = jwtSecurityToken.Claims; - string? objectId = claims.Where((c) => c.Type == "oid" || c.Type.EndsWith("oid", StringComparison.CurrentCulture)).Select((n) => n.Value).FirstOrDefault(); - string? nameId = claims.Where((c) => c.Type == "name" || c.Type.EndsWith("name", StringComparison.CurrentCulture)).Select((n) => n.Value).FirstOrDefault(); - return nameId; - } - public static List GetRequestedRoles(this HttpContext client) { string accessTokenBase64 = client.GetRequestToken(); diff --git a/backend/api/appsettings.Local.json b/backend/api/appsettings.Local.json index ae5190de4..bd34134c7 100644 --- a/backend/api/appsettings.Local.json +++ b/backend/api/appsettings.Local.json @@ -47,5 +47,8 @@ }, "Database": { "UseInMemoryDatabase": true + }, + "Local": { + "DevUserId": "" } } diff --git a/setup.sh b/setup.sh index 927388a74..56fa29a54 100755 --- a/setup.sh +++ b/setup.sh @@ -64,6 +64,26 @@ if [ "$backend_abort" != "true" ]; then dotnet user-secrets set "AzureAd:ClientSecret" $az_client_secret --project backend/api > /dev/null echo -e "Added client secret to ASP.NET secret manager" + echo -e "A username is needed for local development with SignalR" + echo -en "Input a username for yourself (this only needs to be unique within your development environment):\n" + + while [ true ] + do + read -s local_dev_username + + if [ -z "$local_dev_username" ]; then + echo "The local dev username cannot be empty" + echo "Try again:" + else + break + fi + done + + + echo "Local__DevUserId='$local_dev_username'" >> $flotilla_dir/.env + dotnet user-secrets set "Local:DevUserId" $az_client_secret --project backend/api > /dev/null + echo -e "Added local development username to .env file" + echo -e "Backup setup - Done!" echo -e "-----------------------------\n" fi