diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ad90488 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +* text=auto + +*.cs text eol=lf +*.csproj text eol=lf +*.json text eol=lf +*.md text eol=lf +*.razor text eol=lf +*.yml text eol=lf +Dockerfile text eol=lf + +*.jpg binary +*.jpeg binary +*.png binary +*.mp4 binary +*.pdf binary diff --git a/.github/workflows/smartbotweb.yml b/.github/workflows/smartbotweb.yml index 7b11bf8..74e063e 100644 --- a/.github/workflows/smartbotweb.yml +++ b/.github/workflows/smartbotweb.yml @@ -15,7 +15,7 @@ permissions: env: PUBLISH_PATH: published CONFIGURATION: Release - DOTNET_CORE_VERSION: 8.0.x + DOTNET_CORE_VERSION: 10.0.300 PROJECT_PATH: src/server/SmartBotBlazorApp/SmartBotBlazorApp.csproj SOLUTION_PATH: src/server/SmartBotBlazorApp.sln jobs: @@ -28,11 +28,18 @@ jobs: with: dotnet-version: ${{ env.DOTNET_CORE_VERSION }} - name: Restore - run: dotnet restore "${{ env.SOLUTION_PATH }}" + run: dotnet restore "${{ env.SOLUTION_PATH }}" --locked-mode + - name: Verify formatting + run: dotnet format "${{ env.SOLUTION_PATH }}" --verify-no-changes --no-restore + - name: Audit NuGet packages + shell: bash + run: | + dotnet list "${{ env.SOLUTION_PATH }}" package --vulnerable --include-transitive --format json > vulnerable-packages.json + test "$(jq '[.projects[]?.frameworks[]? | ((.topLevelPackages // []) + (.transitivePackages // []))[]] | length' vulnerable-packages.json)" -eq 0 - name: Build run: dotnet build "${{ env.SOLUTION_PATH }}" --configuration ${{ env.CONFIGURATION }} --no-restore - name: Test - run: dotnet test "${{ env.SOLUTION_PATH }}" --configuration ${{ env.CONFIGURATION }} --no-build + run: dotnet test "${{ env.SOLUTION_PATH }}" --configuration ${{ env.CONFIGURATION }} --no-build --collect:"XPlat Code Coverage" --results-directory TestResults - name: Publish run: dotnet publish "${{ env.PROJECT_PATH }}" --configuration ${{ env.CONFIGURATION }} --no-build --output "${{ env.PUBLISH_PATH }}" - name: Publish Artifacts @@ -40,3 +47,15 @@ jobs: with: name: webapp path: ${{ env.PUBLISH_PATH }} + if-no-files-found: error + - name: Publish quality reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: quality-reports + path: | + TestResults + vulnerable-packages.json + if-no-files-found: warn + - name: Build container image + run: docker build --file src/server/SmartBotBlazorApp/Dockerfile --tag smartbotapi:ci . diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..f860221 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + true + + diff --git a/README.md b/README.md index 0082d8c..f2e9a58 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ SmartBotAPI/ ### Web Server -**Prerequisites:** [.NET SDK 8.0](https://dotnet.microsoft.com/download/dotnet/8.0), SQL Server LocalDB on Windows (installed with Visual Studio) or any reachable SQL Server instance. +**Prerequisites:** [.NET SDK 10.0](https://dotnet.microsoft.com/download/dotnet/10.0) (the application targets .NET 8), SQL Server LocalDB on Windows (installed with Visual Studio) or any reachable SQL Server instance. ```powershell cd src/server/SmartBotBlazorApp @@ -171,7 +171,7 @@ The app applies EF Core migrations automatically on startup and listens on: - `https://localhost:7297` - `http://localhost:5221` -On first launch, create a dashboard account at `https://localhost:7297/Account/Register`, then sign in. +The `https` launch profile uses the Development environment, where account registration and the local self-confirmation link are enabled. Create a dashboard account at `https://localhost:7297/Account/Register`, then sign in. Both options are disabled by default outside Development; configure `AccountAccess` explicitly and use a real email sender for a public deployment. To run the automated tests from the repository root: @@ -191,6 +191,8 @@ docker run -p 8080:8080 \ smartbotblazorapp ``` +The container serves plain HTTP on port 8080 unless you explicitly mount and configure an HTTPS certificate. Registration remains disabled in the default production configuration. + ### Robot Firmware **Prerequisites:** Arduino IDE 2.x with the ESP32 board package; an ESP32-C3 DevKitM-1 wired per the schematic in [`docs/schemat.pdf`](docs/schemat.pdf). @@ -226,7 +228,7 @@ The [firmware and hardware reference](docs/firmware.md) links to the manufacture ## Deployment -Pushing to `main` or opening a pull request against it triggers the GitHub Actions workflow (`.github/workflows/smartbotweb.yml`), which builds and tests the solution and publishes a deployable web-app artifact. It does not deploy to an external environment automatically. +Pushing to `main` or opening a pull request against it triggers the GitHub Actions workflow (`.github/workflows/smartbotweb.yml`), which performs a locked restore, verifies formatting, audits NuGet packages, builds and tests with coverage, builds the container image, and publishes a deployable web-app artifact. It does not deploy to an external environment automatically. For the project presentation, the application was deployed to the **smartbotweb** Azure App Service and the firmware targeted `smartbotweb.azurewebsites.net:443`. That service is no longer hosted; the current firmware configuration uses an explicit placeholder that must be replaced with the address of the current server before use. diff --git a/README.pl.md b/README.pl.md index a238af9..d3124b7 100644 --- a/README.pl.md +++ b/README.pl.md @@ -158,7 +158,7 @@ SmartBotAPI/ ### Serwer webowy -**Wymagania:** [.NET SDK 8.0](https://dotnet.microsoft.com/download/dotnet/8.0), SQL Server LocalDB w systemie Windows (instalowany z Visual Studio) lub dowolna dostępna instancja SQL Server. +**Wymagania:** [.NET SDK 10.0](https://dotnet.microsoft.com/download/dotnet/10.0) (aplikacja jest kierowana na .NET 8), SQL Server LocalDB w systemie Windows (instalowany z Visual Studio) lub dowolna dostępna instancja SQL Server. ```powershell cd src/server/SmartBotBlazorApp @@ -172,7 +172,7 @@ Aplikacja automatycznie stosuje migracje EF Core przy starcie i nasłuchuje na: - `https://localhost:7297` - `http://localhost:5221` -Przy pierwszym uruchomieniu utwórz konto panelu pod adresem `https://localhost:7297/Account/Register`, a następnie się zaloguj. +Profil uruchomieniowy `https` korzysta ze środowiska Development, w którym rejestracja i lokalny link samodzielnego potwierdzenia konta są włączone. Utwórz konto panelu pod adresem `https://localhost:7297/Account/Register`, a następnie się zaloguj. Poza Development obie opcje są domyślnie wyłączone; przy publicznym wdrożeniu skonfiguruj jawnie sekcję `AccountAccess` i prawdziwą wysyłkę e-mail. Aby uruchomić testy automatyczne z katalogu głównego repozytorium: @@ -192,6 +192,8 @@ docker run -p 8080:8080 \ smartbotblazorapp ``` +Kontener udostępnia zwykły HTTP na porcie 8080, o ile certyfikat HTTPS nie zostanie jawnie zamontowany i skonfigurowany. W domyślnej konfiguracji produkcyjnej rejestracja pozostaje wyłączona. + ### Firmware robota **Wymagania:** Arduino IDE 2.x z pakietem płytek ESP32; ESP32-C3 DevKitM-1 połączone wg schematu w [`docs/schemat.pdf`](docs/schemat.pdf). @@ -227,7 +229,7 @@ Po połączeniu robot pojawia się na stronie **Live View Image** zalogowanego p ## Wdrożenie -Push do `main` lub otwarcie pull requesta do tej gałęzi uruchamia workflow GitHub Actions (`.github/workflows/smartbotweb.yml`), który buduje i testuje rozwiązanie oraz publikuje artefakt aplikacji webowej gotowy do wdrożenia. Workflow nie wdraża automatycznie do środowiska zewnętrznego. +Push do `main` lub otwarcie pull requesta do tej gałęzi uruchamia workflow GitHub Actions (`.github/workflows/smartbotweb.yml`), który wykonuje odtwarzalny restore, sprawdza formatowanie i podatności pakietów, buduje i testuje rozwiązanie z pomiarem pokrycia, buduje obraz kontenera oraz publikuje artefakt aplikacji webowej. Workflow nie wdraża automatycznie do środowiska zewnętrznego. Na potrzeby prezentacji projektu aplikacja była wdrożona na Azure App Service **smartbotweb**, a firmware łączył się z `smartbotweb.azurewebsites.net:443`. Usługa nie jest już hostowana; aktualna konfiguracja firmware zawiera jawny placeholder, który przed użyciem trzeba zastąpić adresem bieżącego serwera. diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 33b4b62..b60fcdf 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -43,30 +43,28 @@ Changes: the track was trimmed and mixed with the SmartBot driving-test montage. NuGet packages retain their upstream license files and metadata. The direct dependencies declared by the server, client, and test projects, together with -the transitive dependency carrying nonstandard Microsoft terms, are: +the transitive dependencies carrying nonstandard Microsoft terms are: | Package or package group | Version | License | |---|---:|---| -| Azure.Identity | 1.13.1 | [MIT](https://licenses.nuget.org/MIT) | -| Microsoft.AspNetCore.Components.WebAssembly, .Authentication, .Server; Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore; Microsoft.AspNetCore.Identity.EntityFrameworkCore; Microsoft.AspNetCore.SignalR.Client | 8.0.10 | [MIT](https://licenses.nuget.org/MIT) | -| Microsoft.EntityFrameworkCore.SqlServer, Microsoft.EntityFrameworkCore.Tools | 9.0.0 | [MIT](https://licenses.nuget.org/MIT) | -| MudBlazor | 7.15.0 | [MIT](https://licenses.nuget.org/MIT) | -| Newtonsoft.Json | 13.0.3 | [MIT](https://licenses.nuget.org/MIT) | -| System.Formats.Asn1 | 9.0.0 | [MIT](https://licenses.nuget.org/MIT) | +| Microsoft.AspNetCore.Components.WebAssembly, .Authentication, .Server; Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore; Microsoft.AspNetCore.Identity.EntityFrameworkCore; Microsoft.AspNetCore.SignalR.Client | 8.0.29 | [MIT](https://licenses.nuget.org/MIT) | +| Microsoft.EntityFrameworkCore.SqlServer, Microsoft.EntityFrameworkCore.Tools | 9.0.18 | [MIT](https://licenses.nuget.org/MIT) | +| Microsoft.Data.SqlClient | 6.1.6 | [MIT](https://licenses.nuget.org/MIT) | +| MudBlazor | 7.16.0 | [MIT](https://licenses.nuget.org/MIT) | +| Newtonsoft.Json | 13.0.4 | [MIT](https://licenses.nuget.org/MIT) | | SixLabors.ImageSharp | 3.1.12 | Apache-2.0 grant for qualifying open-source use under the [Six Labors Split License 1.0](https://sixlabors.com/posts/license-changes/) | -| Microsoft.Data.SqlClient.SNI.runtime (transitive runtime component) | 5.1.1 | [Microsoft Software License Terms](https://www.nuget.org/packages/Microsoft.Data.SqlClient.SNI.runtime/5.1.1) | -| Microsoft.VisualStudio.Azure.Containers.Tools.Targets (build tooling) | 1.21.0 | [Microsoft Software License Terms](https://www.nuget.org/packages/Microsoft.VisualStudio.Azure.Containers.Tools.Targets/1.21.0) | -| coverlet.collector, Microsoft.NET.Test.Sdk | 10.0.1, 18.7.0 | [MIT](https://licenses.nuget.org/MIT) | -| xunit, xunit.runner.visualstudio | 2.9.3, 3.1.5 | [Apache-2.0](https://licenses.nuget.org/Apache-2.0) | +| Microsoft.Data.SqlClient.SNI.runtime (transitive runtime component) | 6.0.2 | [Microsoft Software License Terms](https://www.nuget.org/packages/Microsoft.Data.SqlClient.SNI.runtime/6.0.2) | +| Microsoft.Identity.Client.NativeInterop (transitive runtime component) | 0.20.6 | [Microsoft Software License Terms](https://www.nuget.org/packages/Microsoft.Identity.Client.NativeInterop/0.20.6) | +| coverlet.collector, Microsoft.NET.Test.Sdk | 10.0.1, 18.8.1 | [MIT](https://licenses.nuget.org/MIT) | +| xunit.v3, xunit.runner.visualstudio | 3.2.2, 3.1.5 | [Apache-2.0](https://licenses.nuget.org/Apache-2.0) | -The restore verified on July 15, 2026 resolves 144 unique package/version pairs +The restore verified on July 15, 2026 resolves 140 unique package/version pairs across the server, client, and test projects. Their NuGet metadata consists of -125 MIT license expressions, 7 Apache-2.0 expressions, 9 legacy upstream -license URLs, and 3 embedded license files. The three embedded licenses are the -SNI runtime, ImageSharp, and the Visual Studio container build tooling listed -above; the legacy URLs belong to Microsoft/.NET Core packages and -`xunit.abstractions`. This accounts for the complete restored graph at that -date. Exact transitive versions can change after a future restore, so each +127 MIT license expressions, 10 Apache-2.0 expressions, no legacy upstream +license URLs, and 3 embedded license files. The embedded licenses are the SNI +runtime, Microsoft Identity native interop, and ImageSharp listed above. +Together, these entries account for the complete restored graph at that date. +Exact transitive versions can change after a future restore, so each package's `.nuspec` metadata and included license file remain authoritative. ## Firmware libraries installed separately @@ -84,10 +82,6 @@ Manager; their source code is not vendored in this repository. ## Externally loaded assets and trademarks -The web application loads Font Awesome Free 6.0.0-beta3 from cdnjs. Font -Awesome Free uses CC BY 4.0 for icons, SIL OFL 1.1 for fonts, and MIT for code; -see the [Font Awesome Free License](https://fontawesome.com/license/free). - `src/server/SmartBotBlazorApp/wwwroot/GitHub_Logo.png` is used only to link to the SmartBotAPI repository. GitHub and the GitHub logo are trademarks of GitHub, Inc.; use is subject to the [GitHub Logo Policy](https://docs.github.com/en/site-policy/other-site-policies/github-logo-policy). diff --git a/docs/architecture.md b/docs/architecture.md index 50fde9e..5e0b39c 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -31,7 +31,7 @@ sequenceDiagram ## Transport - **Protocol:** SignalR JSON hub protocol over WebSocket. The browser uses the official SignalR client; the ESP32 speaks the protocol directly — it performs the JSON handshake (`{"protocol":"json","version":1}`), then exchanges invocation messages (type `1`), each terminated by the SignalR record separator `0x1E`. -- **Security:** dashboards authenticate with the ASP.NET Core Identity cookie. The robot supplies `SECRET_API_KEY` as the SignalR `access_token` query value; the server compares it in constant time with `RobotApiKey` (minimum 32 characters). Production transport uses TLS (`wss://`). +- **Security:** dashboards authenticate with the ASP.NET Core Identity cookie. Interactive Server pages exchange their authenticated session for a five-minute, data-protected operator token; the WebAssembly diagnostics page uses the cookie directly. The robot supplies `SECRET_API_KEY` as the SignalR `access_token` query value, which is compared in constant time with `RobotApiKey` (minimum 32 characters). Hub methods enforce operator-versus-robot roles, and production transport uses TLS (`wss://`). - **Hub endpoint:** `/signalhub` (mapped in `Program.cs`). - **Reconnection:** the firmware retries the WebSocket every 5 s (`WS_RECONNECT_INTERVAL`); response compression is enabled server-side for `application/octet-stream`. @@ -132,4 +132,4 @@ The web application uses Blazor's hybrid rendering: - **Interactive Server** pages (`SmartBotBlazorApp/Components/Pages/`) — live visualization and robot-control pages that hold a SignalR connection server-side. - **Interactive WebAssembly** pages (`SmartBotBlazorApp.Client/Pages/`) — the `/chat` text-diagnostics page connects to the hub directly from WASM. -ASP.NET Core Identity provides the account system (registration, login, 2FA, management pages under `/Account/*`). Receiver and diagnostics pages require authorization. +ASP.NET Core Identity provides the account system (registration, login, 2FA, management pages under `/Account/*`). Receiver and diagnostics pages require authorization. Registration and the no-email confirmation link are available in Development but disabled by default in other environments. diff --git a/docs/getting-started.md b/docs/getting-started.md index 27b76f0..aa4da82 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -6,7 +6,7 @@ This guide walks through setting up the full SmartBotAPI stack: the web server, **Server:** -- [.NET SDK 8.0](https://dotnet.microsoft.com/download/dotnet/8.0) +- [.NET SDK 10.0](https://dotnet.microsoft.com/download/dotnet/10.0) (the projects target .NET 8) - SQL Server — LocalDB (installed with Visual Studio) is enough for development; any SQL Server instance works in production - Optional: Docker Desktop, Visual Studio 2022 @@ -32,10 +32,12 @@ On first start the app creates the database and applies all EF Core migrations a |---|---| | `https` | `https://localhost:7297` (+ `http://localhost:5221`) | | `http` | `http://localhost:5221` | -| Docker | `http://localhost:8080`, `https://localhost:8081` | +| Docker | `http://localhost:8080` | Open the site in a browser — the **Home** page and the navigation menu list all dashboard pages. +The local launch profile uses `Development`, so registration and the no-email self-confirmation link are enabled. In other environments both are disabled by default. `AccountAccess__AllowRegistration` and `AccountAccess__ShowSelfConfirmationLink` can override them; do not expose the self-confirmation link on a public deployment. + ### Custom database Set the environment variable before starting; it overrides `appsettings.json`: @@ -57,7 +59,7 @@ docker run -p 8080:8080 \ smartbotblazorapp ``` -> The container cannot reach LocalDB — always provide a real SQL Server connection string when running in Docker. +> The container cannot reach LocalDB — always provide a real SQL Server connection string when running in Docker. It serves HTTP on port 8080 unless you mount and configure an HTTPS certificate. Registration is disabled by default in Production. ## 2. Flash the Robot Firmware diff --git a/docs/server.md b/docs/server.md index 0f8f5a6..97e4902 100644 --- a/docs/server.md +++ b/docs/server.md @@ -55,7 +55,7 @@ The single real-time endpoint shared by robots and dashboards. See [architecture ### `Data/MeasurementService.cs` -- `SaveMeasurementsToDatabase(user, measurements, avgDistance)` — inserts a `Measurement` row behind an approximately one-write-per-second, process-wide static timestamp check. It is shared across robot IDs and is not a strict synchronized rate limiter. +- `SaveMeasurementsToDatabase(user, measurements, avgDistance)` — validates the seven-value IMU payload and inserts at most one row per second for each robot ID. The in-process throttle is synchronized across concurrent hub calls and uses UTC for elapsed-time comparisons while retaining local timestamps for the existing chart filters. - `RoundMeasurements(measurements, digits)` — display rounding (2 decimal places). - Date-range query used by the charts page. @@ -86,6 +86,8 @@ EF Core context combining ASP.NET Core Identity tables with the `Measurement` te | `ConnectionStrings:DefaultConnection` | SQL Server connection (LocalDB by default) | | `Logging:LogLevel:*` | Standard ASP.NET Core logging levels | | `Api:Url` | External API base URL placeholder | +| `AccountAccess:AllowRegistration` | Enables the registration endpoint; `false` by default, `true` in Development | +| `AccountAccess:ShowSelfConfirmationLink` | Shows the no-email confirmation link; for local development only | | `AllowedHosts` | Host filtering (`*` by default) | ### Environment variables @@ -95,7 +97,8 @@ EF Core context combining ASP.NET Core Identity tables with the `Measurement` te | `SmartBotDBConnectionString` | Overrides the connection string (used in Docker/Azure) | | `RobotApiKey` | URL-safe key (minimum 32 characters) accepted from robot connections to `/signalhub` | | `ASPNETCORE_ENVIRONMENT` | `Development` / `Production` | -| `ASPNETCORE_HTTP_PORTS` / `ASPNETCORE_HTTPS_PORTS` | Container port bindings (8080/8081 in Docker) | +| `AccountAccess__AllowRegistration` / `AccountAccess__ShowSelfConfirmationLink` | Environment-variable overrides for account provisioning | +| `ASPNETCORE_HTTP_PORTS` / `ASPNETCORE_HTTPS_PORTS` | Optional container port bindings; the image serves HTTP on 8080 by default | ### Ports (`Properties/launchSettings.json`) @@ -103,18 +106,18 @@ EF Core context combining ASP.NET Core Identity tables with the `Measurement` te |---|---|---| | Kestrel (`http`/`https`) | 5221 | 7297 | | IIS Express | 24385 | 44312 | -| Docker | 8080 | 8081 | +| Docker (default runtime) | 8080 | — | ## Middleware Pipeline (from `Program.cs`) -Response compression (including `application/octet-stream` for SignalR payloads) → CORS → HTTPS redirection → static files → Identity authentication/authorization → SignalR access guard → antiforgery → Razor components (Server + WASM render modes) → SignalR hub at `/signalhub` → Identity endpoints. +Response compression (including `application/octet-stream` for SignalR payloads) → HTTPS redirection → static files → Identity authentication/authorization → role-aware SignalR access guard → antiforgery → Razor components (Server + WASM render modes) → SignalR hub at `/signalhub` → Identity endpoints. Notes for hardening before production use: -- CORS currently allows any origin — restrict it to the deployed dashboard origins in production. -- `IEmailSender` is a no-op stub (`IdentityNoOpEmailSender`) — plug in a real sender for account confirmation emails. +- Registration is disabled outside Development. Temporarily provision accounts in a trusted environment or connect a controlled administrative flow. +- `IEmailSender` is a no-op stub (`IdentityNoOpEmailSender`) — plug in a real sender before enabling registration publicly. Never enable the self-confirmation link publicly. ## Deployment -- **Docker:** `src/server/SmartBotBlazorApp/Dockerfile` (multi-stage: SDK build → publish → `mcr.microsoft.com/dotnet/aspnet:8.0` runtime, exposing 8080/8081). The project also defines .NET SDK container metadata (`kamilr616/smartbotblazorapp:latest`). -- **CI:** `.github/workflows/smartbotweb.yml` builds and tests the solution and publishes a deployable artifact for pushes and pull requests targeting `main`. External deployment is intentionally manual; the Azure App Service used for the project presentation is no longer hosted. +- **Docker:** `src/server/SmartBotBlazorApp/Dockerfile` (multi-stage .NET 10 SDK build → publish → non-root `mcr.microsoft.com/dotnet/aspnet:8.0` runtime, serving HTTP on 8080). The project also defines .NET SDK container metadata (`kamilr616/smartbotblazorapp:latest`). +- **CI:** `.github/workflows/smartbotweb.yml` performs locked restore, formatting and vulnerability checks, build, tests with coverage, publish, and a container build for pushes and pull requests targeting `main`. External deployment is intentionally manual; the Azure App Service used for the project presentation is no longer hosted. diff --git a/global.json b/global.json new file mode 100644 index 0000000..50baaf1 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.300", + "rollForward": "latestPatch" + } +} diff --git a/src/server/Dockerfile b/src/server/Dockerfile deleted file mode 100644 index 2ff6a3b..0000000 --- a/src/server/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ - -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base -WORKDIR /app -EXPOSE 8080 -EXPOSE 8081 - -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build -WORKDIR /src -COPY ["SmartBotBlazorApp/SmartBotBlazorApp.csproj", "SmartBotBlazorApp/"] -COPY ["SmartBotBlazorApp.Client/SmartBotBlazorApp.Client.csproj", "SmartBotBlazorApp.Client/"] - -RUN dotnet restore "SmartBotBlazorApp/SmartBotBlazorApp.csproj" -COPY . . -RUN dotnet build "SmartBotBlazorApp/SmartBotBlazorApp.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "SmartBotBlazorApp/SmartBotBlazorApp.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "SmartBotBlazorApp.dll"] diff --git a/src/server/SmartBotBlazorApp.Client/Pages/Chat.razor b/src/server/SmartBotBlazorApp.Client/Pages/Chat.razor index c931a30..ab78cdf 100644 --- a/src/server/SmartBotBlazorApp.Client/Pages/Chat.razor +++ b/src/server/SmartBotBlazorApp.Client/Pages/Chat.razor @@ -1,5 +1,5 @@ @page "/chat" -@rendermode InteractiveAuto +@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false)) @implements IAsyncDisposable @inject NavigationManager Navigation @attribute [Authorize] @@ -83,4 +83,4 @@ else await hubConnection.DisposeAsync(); } } -} \ No newline at end of file +} diff --git a/src/server/SmartBotBlazorApp.Client/SmartBotBlazorApp.Client.csproj b/src/server/SmartBotBlazorApp.Client/SmartBotBlazorApp.Client.csproj index a3e70dd..16b08a7 100644 --- a/src/server/SmartBotBlazorApp.Client/SmartBotBlazorApp.Client.csproj +++ b/src/server/SmartBotBlazorApp.Client/SmartBotBlazorApp.Client.csproj @@ -10,10 +10,11 @@ - - - - + + + + + diff --git a/src/server/SmartBotBlazorApp.Client/packages.lock.json b/src/server/SmartBotBlazorApp.Client/packages.lock.json new file mode 100644 index 0000000..fec6e3f --- /dev/null +++ b/src/server/SmartBotBlazorApp.Client/packages.lock.json @@ -0,0 +1,312 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "Microsoft.AspNetCore.Components.WebAssembly": { + "type": "Direct", + "requested": "[8.0.29, )", + "resolved": "8.0.29", + "contentHash": "wAPYLWm1Y2ikdUY0OgsWOJ0A1iQ0GJIvwsgbQKXTBUP/9r5rK3DvmJnZy9a0yEMslJPxvYesACNYDmLz2sTcxQ==", + "dependencies": { + "Microsoft.AspNetCore.Components.Web": "8.0.29", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.Configuration.Json": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.JSInterop.WebAssembly": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.WebAssembly.Authentication": { + "type": "Direct", + "requested": "[8.0.29, )", + "resolved": "8.0.29", + "contentHash": "rs3U4TsemB1lqnpBVtLlQbQM3ewtAImIy3OjlhM7YjxJb/1AEXmIno1Wm8S3HPScYmLZtwLrTG5//uNlYzlRag==", + "dependencies": { + "Microsoft.AspNetCore.Components.Authorization": "8.0.29", + "Microsoft.AspNetCore.Components.Web": "8.0.29" + } + }, + "Microsoft.AspNetCore.SignalR.Client": { + "type": "Direct", + "requested": "[8.0.29, )", + "resolved": "8.0.29", + "contentHash": "GeNFUL39i+NZ+Z8tGFLyqYpdMIwolMfrE7qUbw6ipt864yF4gEDRb/vBoOJhRRaJU6KVdXVuKwUX1s+Atvsprw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Connections.Client": "8.0.29", + "Microsoft.AspNetCore.SignalR.Client.Core": "8.0.29" + } + }, + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[8.0.28, )", + "resolved": "8.0.28", + "contentHash": "XMqgVjlLxLqWmEh3c49haXLQwsMNtvo6YscUaqfvEGfg1iA8hnYgkUVq3i9Zu9gKeNKMWiiZKVwZExc/qyEAsQ==" + }, + "Microsoft.NET.Sdk.WebAssembly.Pack": { + "type": "Direct", + "requested": "[10.0.9, )", + "resolved": "10.0.9", + "contentHash": "VZrxevxXB2tmpyRKTbvVCNUt3GsJiOvWokwa7G328bQf7rKap9cNHPWrtPzTAWNphL7b6byHclNu1jc4QXv46w==" + }, + "Microsoft.AspNetCore.Authorization": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "IMVrMqYF7LASYMWVt/sL9f5vkA8qtfvsLEaxSchEc1Pk/zxdQt1fP1W3hN6Wxm0+wJjAKO+DhF/1mi22AtAfCQ==", + "dependencies": { + "Microsoft.AspNetCore.Metadata": "8.0.29", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.Components": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "1ZNdFYtEORG9LOYFJf0AtNMSAtkQ8WQ5XEGQXdEDvRMYBp4MS5t6mPAiWGVMb5aKpPfaeXOGHQqDcZHP3QfuAA==", + "dependencies": { + "Microsoft.AspNetCore.Authorization": "8.0.29", + "Microsoft.AspNetCore.Components.Analyzers": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.Analyzers": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "AoGUtJ3CgKhihjGCga7vNCawzWUHb7QNMtxzBJKjTYYhM9HwkGE/xmg6sZNO3Tw5W+ke5PoUO411Squ//jgiTQ==" + }, + "Microsoft.AspNetCore.Components.Authorization": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "1zf+E8B8BZod/1POB4c/YnHKPXaj/zkoFeT079iwJHWMBXxB/Y75Dug2KstusYEZBHyiHdN7UUD+Fh2edLEVvg==", + "dependencies": { + "Microsoft.AspNetCore.Authorization": "8.0.29", + "Microsoft.AspNetCore.Components": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.Forms": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "GXmEG6N30oDLZHvyxUmwKDPh/iQkcsktjUb3wffSV3Qi4DD1O37Zzz4EBIsgLQwTr4WtqjQ94yprJtKy+YSE1g==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.Web": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "1El7nDceNvO07z3Wr8uYj9J5Y7KdtIEgpbyfb9r88BYFVuZxaDDA2F4n907mWib1iLbw+XoV25XbNcPH2rne/A==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.29", + "Microsoft.AspNetCore.Components.Forms": "8.0.29", + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Primitives": "8.0.0", + "Microsoft.JSInterop": "8.0.29", + "System.IO.Pipelines": "8.0.0" + } + }, + "Microsoft.AspNetCore.Connections.Abstractions": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "jq7oQyQE4EgWNeK6sHQn/XGyDHQ2pB5JzU4Bam9m1PQ9UtxLI/4AFqXW5Yl9VDZkTf8hkRLcJ1AbkBNCy/NiGQ==", + "dependencies": { + "Microsoft.Extensions.Features": "8.0.29", + "System.IO.Pipelines": "8.0.0" + } + }, + "Microsoft.AspNetCore.Http.Connections.Client": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "ZnUtm+OPtOOKnnF+ZQucdPb/oBix5gyznn+XUztW7Z5c5TpWkwx4NNJVxJGoazMdpQu3EUsXF3anN2nOSK7pNg==", + "dependencies": { + "Microsoft.AspNetCore.Http.Connections.Common": "8.0.29", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.Http.Connections.Common": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "JBJI5k7nXefp2pdlk2cgdQp1zdweCcKzzeXjIalEQDuFdkJ4nMZ/Lzt3NA2XhqCKzKzKSh+EfKMytkYkxdK0NA==", + "dependencies": { + "Microsoft.AspNetCore.Connections.Abstractions": "8.0.29" + } + }, + "Microsoft.AspNetCore.Metadata": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "5Oyb4tncDGft/tXVJoyO7iWy1fnz1AzgycQfzPPSJqafmp3J+xLhgfDSTB+hzVVyG9mcb9z/8m9RDEx7bCj3Kw==" + }, + "Microsoft.AspNetCore.SignalR.Client.Core": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "T7RADZHU0G093e037prxlYIjV09mxIaVXcSip4tcpMMivawMBAR9WIJc9mqhkmAhO8RxmFKaK8BloOzLPuk/9Q==", + "dependencies": { + "Microsoft.AspNetCore.SignalR.Common": "8.0.29", + "Microsoft.AspNetCore.SignalR.Protocols.Json": "8.0.29", + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1", + "System.Threading.Channels": "8.0.0" + } + }, + "Microsoft.AspNetCore.SignalR.Common": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "silO++Rqw7ssetrLnSPTauWwJUwU/sgITKsYpuvJtDrqwZnXLYqvMflKkdS118oPLrh5GgZspvakJoQffbin4Q==", + "dependencies": { + "Microsoft.AspNetCore.Connections.Abstractions": "8.0.29", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.SignalR.Protocols.Json": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "lS8j3Awf+kwG0lGHNIHXJFiCEogdTroLhmRxQ4+T68aJUaXIu0LxVAZL9hjm16LoohL4jVmTPwgQHF7mYKo4Ow==", + "dependencies": { + "Microsoft.AspNetCore.SignalR.Common": "8.0.29" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Features": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "0zMsmVIfuu9AP3pgIbHt3SOdEHHdGZSbRlTl8+ysZkNLbbZ0B2tm0Y6kYfuw/VsuAuKHy0IOOgobm3maYRQCIg==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Microsoft.JSInterop": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "6N3e6a5YHcV53/jU63XBvLEOEV3ctWSFZi+hNl0HNRHYd3aJrTSA9Wl6tq7/9piO+pYRat4V6wPB2NEWMe3TnA==" + }, + "Microsoft.JSInterop.WebAssembly": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "aPT2wbEYTKwFYwSGGNEoX0af5jrAPIG8zzMuZ02Hfh4+6+0lFAdRcZ3GEHljoVbE491WSLgoeyMQTcUKx3zYTQ==", + "dependencies": { + "Microsoft.JSInterop": "8.0.29" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==" + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==" + } + }, + "net8.0/browser-wasm": {} + } +} \ No newline at end of file diff --git a/src/server/SmartBotBlazorApp.Tests/RobotMeasurementThrottleTests.cs b/src/server/SmartBotBlazorApp.Tests/RobotMeasurementThrottleTests.cs new file mode 100644 index 0000000..b4a5f4c --- /dev/null +++ b/src/server/SmartBotBlazorApp.Tests/RobotMeasurementThrottleTests.cs @@ -0,0 +1,50 @@ +using SmartBotBlazorApp.Data; + +namespace SmartBotBlazorApp.Tests; + +public class RobotMeasurementThrottleTests +{ + private static readonly TimeSpan Interval = TimeSpan.FromSeconds(1); + + [Fact] + public void ThrottleIsIndependentForEachRobot() + { + var timeProvider = new ManualTimeProvider(); + var throttle = new RobotMeasurementThrottle(timeProvider); + + Assert.True(throttle.TryAcquire("robot-a", Interval, out _)); + Assert.False(throttle.TryAcquire("robot-a", Interval, out _)); + Assert.True(throttle.TryAcquire("robot-b", Interval, out _)); + + timeProvider.Advance(Interval); + + Assert.True(throttle.TryAcquire("robot-a", Interval, out var timestamp)); + Assert.Equal(timeProvider.GetUtcNow(), timestamp); + } + + [Fact] + public void ConcurrentAttemptsAllowOnlyOneWrite() + { + var throttle = new RobotMeasurementThrottle(new ManualTimeProvider()); + var successes = 0; + + Parallel.For(0, 100, iteration => + { + if (throttle.TryAcquire("robot-a", Interval, out _)) + { + Interlocked.Increment(ref successes); + } + }); + + Assert.Equal(1, successes); + } + + private sealed class ManualTimeProvider : TimeProvider + { + private DateTimeOffset _utcNow = new(2026, 7, 15, 10, 0, 0, TimeSpan.Zero); + + public override DateTimeOffset GetUtcNow() => _utcNow; + + public void Advance(TimeSpan duration) => _utcNow += duration; + } +} diff --git a/src/server/SmartBotBlazorApp.Tests/RobotMessageValidatorTests.cs b/src/server/SmartBotBlazorApp.Tests/RobotMessageValidatorTests.cs index dde785a..b1d0a95 100644 --- a/src/server/SmartBotBlazorApp.Tests/RobotMessageValidatorTests.cs +++ b/src/server/SmartBotBlazorApp.Tests/RobotMessageValidatorTests.cs @@ -40,4 +40,11 @@ public void InvalidTelemetryShapeOrValuesAreRejected() measurements[0] = double.NaN; Assert.Throws(() => RobotMessageValidator.ValidateTelemetry("Robot_KI", measurements, new ushort[64])); } + + [Fact] + public void RobotIdentifierLongerThanDatabaseColumnIsRejected() + { + Assert.Throws(() => + RobotMessageValidator.ValidateTelemetry(new string('r', 51), new double[7], new ushort[64])); + } } diff --git a/src/server/SmartBotBlazorApp.Tests/SignalHubAccessTests.cs b/src/server/SmartBotBlazorApp.Tests/SignalHubAccessTests.cs index 4bd6e51..b79d167 100644 --- a/src/server/SmartBotBlazorApp.Tests/SignalHubAccessTests.cs +++ b/src/server/SmartBotBlazorApp.Tests/SignalHubAccessTests.cs @@ -1,4 +1,5 @@ using System.Security.Claims; +using Microsoft.AspNetCore.DataProtection; namespace SmartBotBlazorApp.Tests; @@ -12,6 +13,7 @@ public void AuthenticatedUserDoesNotNeedRobotKey() var identity = new ClaimsIdentity([new Claim(ClaimTypes.Name, "operator")], "test"); Assert.True(SignalHubAccess.IsAllowed(new ClaimsPrincipal(identity), null, null)); + Assert.True(SignalHubAccess.IsOperator(new ClaimsPrincipal(identity))); } [Fact] @@ -34,4 +36,39 @@ public void ShortConfiguredKeyIsRejected() { Assert.False(SignalHubAccess.IsAllowed(new ClaimsPrincipal(), "short", "short")); } + + [Fact] + public void RobotPrincipalCannotActAsOperator() + { + var principal = SignalHubAccess.CreateRobotPrincipal(); + + Assert.True(SignalHubAccess.IsRobot(principal)); + Assert.False(SignalHubAccess.IsOperator(principal)); + } + + [Fact] + public void UnauthenticatedRobotClaimIsRejected() + { + var identity = new ClaimsIdentity( + [new Claim(SignalHubAccess.ClientTypeClaim, SignalHubAccess.RobotClientType)]); + + Assert.False(SignalHubAccess.IsRobot(new ClaimsPrincipal(identity))); + } + + [Fact] + public void OperatorTokenRoundTripsAndRejectsTampering() + { + var service = new SignalHubTokenService(new EphemeralDataProtectionProvider()); + var identity = new ClaimsIdentity( + [new Claim(ClaimTypes.NameIdentifier, "operator-1")], + "test"); + + var token = service.CreateOperatorToken(new ClaimsPrincipal(identity)); + var principal = service.TryValidateOperatorToken(token); + + Assert.NotNull(principal); + Assert.True(SignalHubAccess.IsOperator(principal)); + Assert.Equal("operator-1", principal.FindFirstValue(ClaimTypes.NameIdentifier)); + Assert.Null(service.TryValidateOperatorToken(token + "tampered")); + } } diff --git a/src/server/SmartBotBlazorApp.Tests/SmartBotBlazorApp.Tests.csproj b/src/server/SmartBotBlazorApp.Tests/SmartBotBlazorApp.Tests.csproj index 2561f3e..73d5104 100644 --- a/src/server/SmartBotBlazorApp.Tests/SmartBotBlazorApp.Tests.csproj +++ b/src/server/SmartBotBlazorApp.Tests/SmartBotBlazorApp.Tests.csproj @@ -14,8 +14,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/server/SmartBotBlazorApp.Tests/packages.lock.json b/src/server/SmartBotBlazorApp.Tests/packages.lock.json new file mode 100644 index 0000000..fdecf4e --- /dev/null +++ b/src/server/SmartBotBlazorApp.Tests/packages.lock.json @@ -0,0 +1,879 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "27jXSV/0DbVqF5jDrAxuQFZ9oaz6gmG03p8ttxAFk+X0M4woFYj7MoWDLCna5EGLb0CE6OE7X6ZH3Wt5smTtaA==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[18.8.1, )", + "resolved": "18.8.1", + "contentHash": "dknJL3/9Y3t4XuCBqnc0PevPxgLsUMmVhjwup/b1HNovA8zWcj3XsfIf7c6p05363DWcqL7X/YhDL9B+Zymv1w==", + "dependencies": { + "Microsoft.CodeCoverage": "18.8.1", + "Microsoft.TestPlatform.TestHost": "18.8.1" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" + }, + "xunit.v3": { + "type": "Direct", + "requested": "[3.2.2, )", + "resolved": "3.2.2", + "contentHash": "L+4/4y0Uqcg8/d6hfnxhnwh4j9FaeULvefTwrk30rr1o4n/vdPfyUQ8k0yzH8VJx7bmFEkDdcRfbtbjEHlaYcA==", + "dependencies": { + "xunit.v3.mtp-v1": "[3.2.2]" + } + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.50.0", + "contentHash": "GBNKZEhdIbTXxedvD3R7I/yDVFX9jJJEz02kCziFSJxspSQ5RMHc3GktulJ1s7+ffXaXD7kMgrtdQTaggyInLw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.8.0", + "System.Memory.Data": "8.0.1" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.17.1", + "contentHash": "MSZkBrctcpiGxs9Cvr2VKKoN6qFLZlP3I6xuCWJ9iTgitI5Rgxtk5gfOSpXPZE3+CJmZ/mnqpQyGyjawFn5Vvg==", + "dependencies": { + "Azure.Core": "1.50.0", + "Microsoft.Identity.Client": "4.78.0", + "Microsoft.Identity.Client.Extensions.Msal": "4.78.0" + } + }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.23.0", + "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.AspNetCore.Authorization": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "IMVrMqYF7LASYMWVt/sL9f5vkA8qtfvsLEaxSchEc1Pk/zxdQt1fP1W3hN6Wxm0+wJjAKO+DhF/1mi22AtAfCQ==", + "dependencies": { + "Microsoft.AspNetCore.Metadata": "8.0.29", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.Components": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "1ZNdFYtEORG9LOYFJf0AtNMSAtkQ8WQ5XEGQXdEDvRMYBp4MS5t6mPAiWGVMb5aKpPfaeXOGHQqDcZHP3QfuAA==", + "dependencies": { + "Microsoft.AspNetCore.Authorization": "8.0.29", + "Microsoft.AspNetCore.Components.Analyzers": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.Analyzers": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "AoGUtJ3CgKhihjGCga7vNCawzWUHb7QNMtxzBJKjTYYhM9HwkGE/xmg6sZNO3Tw5W+ke5PoUO411Squ//jgiTQ==" + }, + "Microsoft.AspNetCore.Components.Authorization": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "1zf+E8B8BZod/1POB4c/YnHKPXaj/zkoFeT079iwJHWMBXxB/Y75Dug2KstusYEZBHyiHdN7UUD+Fh2edLEVvg==", + "dependencies": { + "Microsoft.AspNetCore.Authorization": "8.0.29", + "Microsoft.AspNetCore.Components": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.Forms": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "GXmEG6N30oDLZHvyxUmwKDPh/iQkcsktjUb3wffSV3Qi4DD1O37Zzz4EBIsgLQwTr4WtqjQ94yprJtKy+YSE1g==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.Web": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "1El7nDceNvO07z3Wr8uYj9J5Y7KdtIEgpbyfb9r88BYFVuZxaDDA2F4n907mWib1iLbw+XoV25XbNcPH2rne/A==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.29", + "Microsoft.AspNetCore.Components.Forms": "8.0.29", + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Primitives": "8.0.0", + "Microsoft.JSInterop": "8.0.29", + "System.IO.Pipelines": "8.0.0" + } + }, + "Microsoft.AspNetCore.Components.WebAssembly": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "wAPYLWm1Y2ikdUY0OgsWOJ0A1iQ0GJIvwsgbQKXTBUP/9r5rK3DvmJnZy9a0yEMslJPxvYesACNYDmLz2sTcxQ==", + "dependencies": { + "Microsoft.AspNetCore.Components.Web": "8.0.29", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.Configuration.Json": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.JSInterop.WebAssembly": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.WebAssembly.Authentication": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "rs3U4TsemB1lqnpBVtLlQbQM3ewtAImIy3OjlhM7YjxJb/1AEXmIno1Wm8S3HPScYmLZtwLrTG5//uNlYzlRag==", + "dependencies": { + "Microsoft.AspNetCore.Components.Authorization": "8.0.29", + "Microsoft.AspNetCore.Components.Web": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.WebAssembly.Server": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "mF8RihuRK1PqMDDXCmD53f14Q1zp6N51UFwGLDjwiKpGEf4MWUknbRIglPIZf3Lt6c/af2pNSz1fLYXcymzNCg==" + }, + "Microsoft.AspNetCore.Connections.Abstractions": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "jq7oQyQE4EgWNeK6sHQn/XGyDHQ2pB5JzU4Bam9m1PQ9UtxLI/4AFqXW5Yl9VDZkTf8hkRLcJ1AbkBNCy/NiGQ==", + "dependencies": { + "Microsoft.Extensions.Features": "8.0.29", + "System.IO.Pipelines": "8.0.0" + } + }, + "Microsoft.AspNetCore.Cryptography.Internal": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "RAknfruzMmottkLWOxKzCrqOE5hp88Avr8qJU9YtxYsiJ1aH+Ij2SVcwS9Tx3VXuUesJqPADK6gZsG/bX4h6XA==" + }, + "Microsoft.AspNetCore.Cryptography.KeyDerivation": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "DIEnc7iZJHoNIar8pz+HHtm9XvshFrCSq9GtS+KLQn6AA98c9PeeoSPCwTBwvfY47KpuTUr6zNYIy/EERMzf3w==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.Internal": "8.0.29" + } + }, + "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "S2oPwtyDpmRQTjPY7M/8kDqH2Eujd8FpkIav4tdgYdTM6LeM5ab+L2IklpQ+TUbtbdyItIooKHVEzbuk1pxUDg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Relational": "8.0.29" + } + }, + "Microsoft.AspNetCore.Http.Connections.Client": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "ZnUtm+OPtOOKnnF+ZQucdPb/oBix5gyznn+XUztW7Z5c5TpWkwx4NNJVxJGoazMdpQu3EUsXF3anN2nOSK7pNg==", + "dependencies": { + "Microsoft.AspNetCore.Http.Connections.Common": "8.0.29", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.Http.Connections.Common": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "JBJI5k7nXefp2pdlk2cgdQp1zdweCcKzzeXjIalEQDuFdkJ4nMZ/Lzt3NA2XhqCKzKzKSh+EfKMytkYkxdK0NA==", + "dependencies": { + "Microsoft.AspNetCore.Connections.Abstractions": "8.0.29" + } + }, + "Microsoft.AspNetCore.Identity.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "TYM50l/64ZShlAE2jChEQapK0KPg969yYKGR8w8KGxVtktRlaNC/aN43bggoYwswiXhQ7dU2zqmMu6UH2pVbYQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Relational": "8.0.29", + "Microsoft.Extensions.Identity.Stores": "8.0.29" + } + }, + "Microsoft.AspNetCore.Metadata": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "5Oyb4tncDGft/tXVJoyO7iWy1fnz1AzgycQfzPPSJqafmp3J+xLhgfDSTB+hzVVyG9mcb9z/8m9RDEx7bCj3Kw==" + }, + "Microsoft.AspNetCore.SignalR.Client": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "GeNFUL39i+NZ+Z8tGFLyqYpdMIwolMfrE7qUbw6ipt864yF4gEDRb/vBoOJhRRaJU6KVdXVuKwUX1s+Atvsprw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Connections.Client": "8.0.29", + "Microsoft.AspNetCore.SignalR.Client.Core": "8.0.29" + } + }, + "Microsoft.AspNetCore.SignalR.Client.Core": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "T7RADZHU0G093e037prxlYIjV09mxIaVXcSip4tcpMMivawMBAR9WIJc9mqhkmAhO8RxmFKaK8BloOzLPuk/9Q==", + "dependencies": { + "Microsoft.AspNetCore.SignalR.Common": "8.0.29", + "Microsoft.AspNetCore.SignalR.Protocols.Json": "8.0.29", + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1", + "System.Threading.Channels": "8.0.0" + } + }, + "Microsoft.AspNetCore.SignalR.Common": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "silO++Rqw7ssetrLnSPTauWwJUwU/sgITKsYpuvJtDrqwZnXLYqvMflKkdS118oPLrh5GgZspvakJoQffbin4Q==", + "dependencies": { + "Microsoft.AspNetCore.Connections.Abstractions": "8.0.29", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.SignalR.Protocols.Json": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "lS8j3Awf+kwG0lGHNIHXJFiCEogdTroLhmRxQ4+T68aJUaXIu0LxVAZL9hjm16LoohL4jVmTPwgQHF7mYKo4Ow==", + "dependencies": { + "Microsoft.AspNetCore.SignalR.Common": "8.0.29" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "18.8.1", + "contentHash": "Eclse/ZZjr4lmWzZFNN9h/OluhKL+SK/QbUyKUewgX139aGeyMEO/DkMPwuFs2MixvanTnz6891rF8UHDg+W4Q==" + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "6.1.6", + "contentHash": "6abRPXrjjEWxcNkTomBIzBdGolYnwD0ykP+6YAynaEEfKlGe94ZSQd58pggucusp28xUA7wLo9XcvzU92ZuF6Q==", + "dependencies": { + "Azure.Core": "1.50.0", + "Azure.Identity": "1.17.1", + "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Identity.Client": "4.84.2", + "Microsoft.Identity.Client.Broker": "4.84.2", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "8.0.1", + "System.Diagnostics.DiagnosticSource": "8.0.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1", + "System.Security.Cryptography.Pkcs": "8.0.1" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "ByxJvHZwP1QWLXmPLNClJ4mVAgzAjzdKlK/pb2E9qbyuQdhwGTy/yfTDsPlwYtXlwnDes9/2p4mOP/+Cbo4pFA==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "9.0.18", + "Microsoft.EntityFrameworkCore.Analyzers": "9.0.18", + "Microsoft.Extensions.Caching.Memory": "9.0.18", + "Microsoft.Extensions.Logging": "9.0.18" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "4nD2ZtEG/fNyHT4bo0MovOw6wDVv2xunhmRm448AKbX3y2ohEZkkxI4Nm+JHBZmluzsV6HHWFRWawFj8VuNltg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "KI+qKZHU1OfEUjQEpQEh/EXo9wN8DP+Jt1aReNJe/MezR/cVTr79c4VeMv4cyizki7YWW3DDDWZCcy/Eiay6dg==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "kn6DongUAVevGsydWJdJUB0N6QamtwyDjhwrbV8Yrfs84i5rcEj5SY+3eyPzAInzd1icrZBd7A+r0yEpULkjEg==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "9.0.18", + "Microsoft.Extensions.Caching.Memory": "9.0.18", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.18", + "Microsoft.Extensions.Logging": "9.0.18" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "ahPFWSNFE13PlCF9E7Hu2oUHydCItn4j4S71P00vV56PDKnY59nlg3gpSNTblYUvi5mwOPWtIAOud8hZOBfrQA==", + "dependencies": { + "Microsoft.Data.SqlClient": "5.1.6", + "Microsoft.EntityFrameworkCore.Relational": "9.0.18", + "Microsoft.Extensions.Caching.Memory": "9.0.18", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.18", + "Microsoft.Extensions.Logging": "9.0.18", + "System.Formats.Asn1": "9.0.18", + "System.Text.Json": "9.0.18" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "Y8PnPKq+ASBazn0QA5d98/dWT0MAfv9CJYV6zPZ/TisZpAUe5zWgKldJLXkrmUuIzvUpQS7/SF7+1dld3OP/EQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.18" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "ZQ+x93CRR8DOGT+YPP4YXvwTAJ0MfvAghs5cwCSvtZFiORSfprAw9f67d0QDhf6PCIYyFsMKFB+PI3p9lpiMUQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "9.0.18", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.18", + "Microsoft.Extensions.Logging.Abstractions": "9.0.18", + "Microsoft.Extensions.Options": "9.0.18", + "Microsoft.Extensions.Primitives": "9.0.18" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "jgC5SZeK94t8DXl836ifIM5gScNqknkOwISrY503eWg7AHBrv+fnwty+C9n35H4WvBjs8OY21jPlRRf7ESBK/g==", + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.18" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "xPoYhpBypsweLpZ29GPp6Kv6g1fZMUwJ800D/xO7ASuNn5zx0BNIP600wXXhbPNeO8kKngFRtoWplRffxBUDgw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.18" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "eSUdaLUP+pj1J7SpCsUYQP6dNk0w9g0KGu4ovPYBnlfqomW1u+1fF8ZIjMVuHY6Z3p30yL0SxND9JTS6i3NolQ==" + }, + "Microsoft.Extensions.Features": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "0zMsmVIfuu9AP3pgIbHt3SOdEHHdGZSbRlTl8+ysZkNLbbZ0B2tm0Y6kYfuw/VsuAuKHy0IOOgobm3maYRQCIg==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Identity.Core": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "os0Bq475TseOIY+ieTPW/0Iyoe9B+qEr+ahipj9ojpvPgA95tRZXYGaEHSvjv71/AqRgFdN+gSi3AExT3wih7g==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.KeyDerivation": "8.0.29", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Identity.Stores": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "AbZ/yMoGstUaDucifJ25/Dm344GuQv8LYLrIsPTkGZ/H+Nb1/oyfJXV+acQ3Gon6uNqvXso4W/xqqiSTkUqsvQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.Identity.Core": "8.0.29", + "Microsoft.Extensions.Logging": "8.0.1" + } + }, + "Microsoft.Extensions.Localization": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "n32ZGmc2UOv9R/xsBHQPOxBsqCSAAJSoMtFqoQ8zOegB2P+H6+773OyCWg4jRBkO/3dmCyBJXB0yLAOVW2/C/w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1", + "Microsoft.Extensions.Localization.Abstractions": "8.0.8", + "Microsoft.Extensions.Logging.Abstractions": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Localization.Abstractions": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "WYIsqP/A6dH/LWJznrvgoNPc7Q+CUJD0E78765GL3aonBtyFK1BKtPzBvvlnrr3SVqSO2r6xJCLgCEiCMG1gfA==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "LuOCZhcJ4TeCLE99TRqI1FHReB29G7bAPInrAAG3E85/S+E4mcrmaoeoXLnOUi6DEosx87pauyFNICdiFIFqkA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "9.0.18", + "Microsoft.Extensions.Logging.Abstractions": "9.0.18", + "Microsoft.Extensions.Options": "9.0.18" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "uzNHNdNZJTHACwFkWclx8s5GXLjGacJj4QwBQZ/6BeixTJjlHmNESIK738iPFLPkO8jWsENLUcUW/kAPe0b8ew==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.18", + "System.Diagnostics.DiagnosticSource": "9.0.18" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "HiA/R0jGR10TGuR6coM8KMUdJ5YlP3bhGxqEdGLkxFdBV1EFNdZMAajSEZHwkex4X/iMQwRCQVotqpElcxOImg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.18", + "Microsoft.Extensions.Primitives": "9.0.18" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "hfHudMC5zDlwMrC0HiHOJesSHMvM+CdqjomjcV/YVzFq5dfSpBRvyRLm1n1Bfh41ZpQnyJzqX+YEo95BAmcDAQ==" + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.84.2", + "contentHash": "Va9FjmABSgj/lcUfHH0pBNQkmS7SNyepz2ERV7Yynp6QgEYpFdSqR6Vzy1WWipN8GS5pBHuXoZbqaDWuARCrHQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "8.14.0", + "System.Diagnostics.DiagnosticSource": "6.0.1" + } + }, + "Microsoft.Identity.Client.Broker": { + "type": "Transitive", + "resolved": "4.84.2", + "contentHash": "928ySLeGz1xtvydwq6h9tv7TbxrtA4ZzavLKUqRJh0PW6BGcEJwpI8W8vf2PlQdYemGm/oyGjdzYzdY/I8r/4A==", + "dependencies": { + "Microsoft.Identity.Client": "4.84.2", + "Microsoft.Identity.Client.NativeInterop": "0.20.6" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.78.0", + "contentHash": "DYU9o+DrDQuyZxeq91GBA9eNqBvA3ZMkLzQpF7L9dTk6FcIBM1y1IHXWqiKXTvptPF7CZE59upbyUoa+FJ5eiA==", + "dependencies": { + "Microsoft.Identity.Client": "4.78.0", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.Identity.Client.NativeInterop": { + "type": "Transitive", + "resolved": "0.20.6", + "contentHash": "noyfdMfVxyWpM6xUDmlW6gUvZ5ci24z7Qoy15JvynTQfZV5vTD2NEdsnT5Gy4ngViwgdIp5iRjRy10jAKXFAgw==" + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "8.14.0", + "contentHash": "iwbCpSjD3ehfTwBhtSNEtKPK0ICun6ov7Ibx6ISNA9bfwIyzI2Siwyi9eJFCJBwxowK9xcA1mj+jBWiigeqgcQ==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.7.1" + } + }, + "Microsoft.JSInterop": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "6N3e6a5YHcV53/jU63XBvLEOEV3ctWSFZi+hNl0HNRHYd3aJrTSA9Wl6tq7/9piO+pYRat4V6wPB2NEWMe3TnA==" + }, + "Microsoft.JSInterop.WebAssembly": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "aPT2wbEYTKwFYwSGGNEoX0af5jrAPIG8zzMuZ02Hfh4+6+0lFAdRcZ3GEHljoVbE491WSLgoeyMQTcUKx3zYTQ==", + "dependencies": { + "Microsoft.JSInterop": "8.0.29" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "Microsoft.Testing.Extensions.Telemetry": { + "type": "Transitive", + "resolved": "1.9.1", + "contentHash": "No5AudZMmSb+uNXjlgL2y3/stHD2IT4uxqc5yHwkE+/nNux9jbKcaJMvcp9SwgP4DVD8L9/P3OUz8mmmcvEIdQ==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.23.0", + "Microsoft.Testing.Platform": "1.9.1" + } + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "1.9.1", + "contentHash": "AL46Xe1WBi85Ntd4mNPvat5ZSsZ2uejiVqoKCypr8J3wK0elA5xJ3AN4G/Q4GIwzUFnggZoH/DBjnr9J18IO/g==", + "dependencies": { + "Microsoft.Testing.Platform": "1.9.1" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "1.9.1", + "contentHash": "QafNtNSmEI0zazdebnsIkDKmFtTSpmx/5PLOjURWwozcPb3tvRxzosQSL8xwYNM1iPhhKiBksXZyRSE2COisrA==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "1.9.1", + "contentHash": "oTUtyR4X/s9ytuiNA29FGsNCCH0rNmY5Wdm14NCKLjTM1cT9edVSlA+rGS/mVmusPqcP0l/x9qOnMXg16v87RQ==", + "dependencies": { + "Microsoft.Testing.Platform": "1.9.1" + } + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "18.8.1", + "contentHash": "qLbktNB1+b1XZLNJBTzaWVVJAd6PEzD7cgD406geMb6PcFZhp3EDNa1tctWx1+mtMU6MP/6ozVvFPC9vs2a9rw==", + "dependencies": { + "System.Reflection.Metadata": "8.0.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "18.8.1", + "contentHash": "FaQHPDTUOcE+SFTjssNPfrub2lT9Zyon4J2W/KLHt/efLJACb1TCeWXyOgh0D/4Q1e4n+S3E6mOKud+9nLZlEA==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "18.8.1" + } + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MudBlazor": { + "type": "Transitive", + "resolved": "7.16.0", + "contentHash": "68bEFn7MLCyOB4yYxtvZG+AA+bZbxBDcIOhnSVFrHrAg8LLXVw1LxHCp9EyRktlREpnoAO1JxINp+5WeHc9Z4w==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.8", + "Microsoft.AspNetCore.Components.Web": "8.0.8", + "Microsoft.Extensions.Localization": "8.0.8" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + }, + "SixLabors.ImageSharp": { + "type": "Transitive", + "resolved": "3.1.12", + "contentHash": "iAg6zifihXEFS/t7fiHhZBGAdCp3FavsF4i2ZIDp0JfeYeDVzvmlbY1CNhhIKimaIzrzSi5M/NBFcWvZT2rB/A==" + }, + "System.ClientModel": { + "type": "Transitive", + "resolved": "1.8.0", + "contentHash": "AqRzhn0v29GGGLj/Z6gKq4lGNtvPHT4nHdG5PDJh9IfVjv/nYUVmX11hwwws1vDFeIAzrvmn0dPu8IjLtu6fAw==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "System.Memory.Data": "8.0.1" + } + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "gPYFPDyohW2gXNhdQRSjtmeS6FymL2crg4Sral1wtvEJ7DUqFCDWDVbbLobASbzxfic8U1hQEdC7hmg9LHncMw==", + "dependencies": { + "System.Diagnostics.EventLog": "8.0.1", + "System.Security.Cryptography.ProtectedData": "8.0.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "zXv17I0lirlmcrLyvSYUHYpXDh/pJ54QfKI96/132KEMsgaG2m61UisI9CJrayZq1mRQL1u4T2ri6eVM6auhPg==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==" + }, + "System.Formats.Asn1": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "FHZkK31ffB8aVfqhh24E2p96Ezihj2d2kE8Ksf6hcPb0uP1kRW9XTAFHa5Y3Ph4kKCdl2TGH8dCr/cHG7TDK6g==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "9AcBFLF9KZpCiv+xTN8emSsiF1FEq/DdswZrJgqlf37dKThJSXwCQ1QUfH24+k9X89C80CI0IQCZatdg0yj3Jw==" + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", + "dependencies": { + "System.Collections.Immutable": "8.0.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "CoCRHFym33aUSf/NtWSVSZa99dkd0Hm7OCZUxORBjRB16LNhIEOf8THPqzIYlvKM0nNDAPTRBa1FxEECrgaxxA==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "Uqybp2r2zFvSDKckcr30TbXRrjW16ihRFzdHVC8Jo8DwkRDJfBuPkXS7hqcUwRYE3ItHKeyfcbq/SpExNPuE2Q==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "/xX8vajlGUp1FoGSNUJBJS+E/2TQjmcMoUzHePa+coPdLnFGPcolRHlybSZhKyAKj+sNDSFlLNYYLe8aEOElfA==", + "dependencies": { + "System.IO.Pipelines": "9.0.18", + "System.Text.Encodings.Web": "9.0.18" + } + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.27.0", + "contentHash": "y/pxIQaLvk/kxAoDkZW9GnHLCEqzwl5TW0vtX3pweyQpjizB9y3DXhb9pkw2dGeUqhLjsxvvJM1k89JowU6z3g==" + }, + "xunit.v3.assert": { + "type": "Transitive", + "resolved": "3.2.2", + "contentHash": "BPciBghgEEaJN/JG00QfCYDfEfnLgQhfnYEy+j1izoeHVNYd5+3Wm8GJ6JgYysOhpBPYGE+sbf75JtrRc7jrdA==" + }, + "xunit.v3.common": { + "type": "Transitive", + "resolved": "3.2.2", + "contentHash": "Hj775PEH6GTbbg0wfKRvG2hNspDCvTH9irXhH4qIWgdrOSV1sQlqPie+DOvFeigsFg2fxSM3ZAaaCDQs+KreFA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "xunit.v3.core.mtp-v1": { + "type": "Transitive", + "resolved": "3.2.2", + "contentHash": "Ga5aA2Ca9ktz+5k3g5ukzwfexwoqwDUpV6z7atSEUvqtd6JuybU1XopHqg1oFd78QdTfZgZE9h5sHpO4qYIi5w==", + "dependencies": { + "Microsoft.Testing.Extensions.Telemetry": "1.9.1", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.9.1", + "Microsoft.Testing.Platform": "1.9.1", + "Microsoft.Testing.Platform.MSBuild": "1.9.1", + "xunit.v3.extensibility.core": "[3.2.2]", + "xunit.v3.runner.inproc.console": "[3.2.2]" + } + }, + "xunit.v3.extensibility.core": { + "type": "Transitive", + "resolved": "3.2.2", + "contentHash": "srY8z/oMPvh/t8axtO2DwrHajhFMH7tnqKildvYrVQIfICi8fOn3yIBWkVPAcrKmHMwvXRJ/XsQM3VMR6DOYfQ==", + "dependencies": { + "xunit.v3.common": "[3.2.2]" + } + }, + "xunit.v3.mtp-v1": { + "type": "Transitive", + "resolved": "3.2.2", + "contentHash": "O41aAzYKBT5PWqATa1oEWVNCyEUypFQ4va6K0kz37dduV3EKzXNMaV2UnEhufzU4Cce1I33gg0oldS8tGL5I0A==", + "dependencies": { + "xunit.analyzers": "1.27.0", + "xunit.v3.assert": "[3.2.2]", + "xunit.v3.core.mtp-v1": "[3.2.2]" + } + }, + "xunit.v3.runner.common": { + "type": "Transitive", + "resolved": "3.2.2", + "contentHash": "/hkHkQCzGrugelOAehprm7RIWdsUFVmIVaD6jDH/8DNGCymTlKKPTbGokD5czbAfqfex47mBP0sb0zbHYwrO/g==", + "dependencies": { + "Microsoft.Win32.Registry": "[5.0.0]", + "xunit.v3.common": "[3.2.2]" + } + }, + "xunit.v3.runner.inproc.console": { + "type": "Transitive", + "resolved": "3.2.2", + "contentHash": "ulWOdSvCk+bPXijJZ73bth9NyoOHsAs1ZOvamYbCkD4DNLX/Bd29Ve2ZNUwBbK0MqfIYWXHZViy/HKrdEC/izw==", + "dependencies": { + "xunit.v3.extensibility.core": "[3.2.2]", + "xunit.v3.runner.common": "[3.2.2]" + } + }, + "smartbotblazorapp": { + "type": "Project", + "dependencies": { + "Microsoft.AspNetCore.Components.WebAssembly.Server": "[8.0.29, )", + "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "[8.0.29, )", + "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "[8.0.29, )", + "Microsoft.AspNetCore.SignalR.Client": "[8.0.29, )", + "Microsoft.Data.SqlClient": "[6.1.6, )", + "Microsoft.EntityFrameworkCore.SqlServer": "[9.0.18, )", + "MudBlazor": "[7.16.0, )", + "Newtonsoft.Json": "[13.0.4, )", + "SixLabors.ImageSharp": "[3.1.12, )", + "SmartBotBlazorApp.Client": "[1.0.0, )" + } + }, + "smartbotblazorapp.client": { + "type": "Project", + "dependencies": { + "Microsoft.AspNetCore.Components.WebAssembly": "[8.0.29, )", + "Microsoft.AspNetCore.Components.WebAssembly.Authentication": "[8.0.29, )", + "Microsoft.AspNetCore.SignalR.Client": "[8.0.29, )" + } + } + } + } +} \ No newline at end of file diff --git a/src/server/SmartBotBlazorApp/AccountAccessOptions.cs b/src/server/SmartBotBlazorApp/AccountAccessOptions.cs new file mode 100644 index 0000000..17b9735 --- /dev/null +++ b/src/server/SmartBotBlazorApp/AccountAccessOptions.cs @@ -0,0 +1,10 @@ +namespace SmartBotBlazorApp; + +public sealed class AccountAccessOptions +{ + public const string SectionName = "AccountAccess"; + + public bool AllowRegistration { get; set; } + + public bool ShowSelfConfirmationLink { get; set; } +} diff --git a/src/server/SmartBotBlazorApp/Components/Account/Pages/Register.razor b/src/server/SmartBotBlazorApp/Components/Account/Pages/Register.razor index b18be84..949253a 100644 --- a/src/server/SmartBotBlazorApp/Components/Account/Pages/Register.razor +++ b/src/server/SmartBotBlazorApp/Components/Account/Pages/Register.razor @@ -14,45 +14,55 @@ @inject ILogger Logger @inject NavigationManager NavigationManager @inject IdentityRedirectManager RedirectManager +@inject Microsoft.Extensions.Options.IOptions AccountAccess Register

Register

-
-
- - - -

Create a new account.

-
- -
- - - -
-
- - - -
-
- - - -
- -
+@if (!AccountAccess.Value.AllowRegistration) +{ +
+ Public account registration is disabled. Ask the administrator to create or enable an account.
-
-
-

Use another service to register.

-
- -
+} +else +{ +
+
+ + + +

Create a new account.

+
+ +
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+
+

Use another service to register.

+
+ +
+
-
+} @code { private IEnumerable? identityErrors; @@ -67,6 +77,19 @@ public async Task RegisterUser(EditContext editContext) { + if (!AccountAccess.Value.AllowRegistration) + { + identityErrors = + [ + new IdentityError + { + Code = "RegistrationDisabled", + Description = "Public account registration is disabled." + } + ]; + return; + } + var user = CreateUser(); await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); diff --git a/src/server/SmartBotBlazorApp/Components/Account/Pages/RegisterConfirmation.razor b/src/server/SmartBotBlazorApp/Components/Account/Pages/RegisterConfirmation.razor index 9654fe6..3459d99 100644 --- a/src/server/SmartBotBlazorApp/Components/Account/Pages/RegisterConfirmation.razor +++ b/src/server/SmartBotBlazorApp/Components/Account/Pages/RegisterConfirmation.razor @@ -9,6 +9,7 @@ @inject IEmailSender EmailSender @inject NavigationManager NavigationManager @inject IdentityRedirectManager RedirectManager +@inject Microsoft.Extensions.Options.IOptions AccountAccess Register confirmation @@ -25,7 +26,7 @@ } else { -

Please check your email to confirm your account.

+

If the account exists, follow the confirmation instructions sent by the application administrator.

} @code { @@ -51,12 +52,10 @@ else var user = await UserManager.FindByEmailAsync(Email); if (user is null) { - HttpContext.Response.StatusCode = StatusCodes.Status404NotFound; - statusMessage = "Error finding user for unspecified email"; + statusMessage = "Unable to prepare account confirmation."; } - else if (EmailSender is IdentityNoOpEmailSender) + else if (EmailSender is IdentityNoOpEmailSender && AccountAccess.Value.ShowSelfConfirmationLink) { - // Once you add a real email sender, you should remove this code that lets you confirm the account var userId = await UserManager.GetUserIdAsync(user); var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); diff --git a/src/server/SmartBotBlazorApp/Components/App.razor b/src/server/SmartBotBlazorApp/Components/App.razor index 4ead12d..8abf03f 100644 --- a/src/server/SmartBotBlazorApp/Components/App.razor +++ b/src/server/SmartBotBlazorApp/Components/App.razor @@ -8,7 +8,6 @@ - diff --git a/src/server/SmartBotBlazorApp/Components/Layout/MainLayout.razor b/src/server/SmartBotBlazorApp/Components/Layout/MainLayout.razor index e0b16a2..0557815 100644 --- a/src/server/SmartBotBlazorApp/Components/Layout/MainLayout.razor +++ b/src/server/SmartBotBlazorApp/Components/Layout/MainLayout.razor @@ -7,16 +7,16 @@
diff --git a/src/server/SmartBotBlazorApp/Components/Pages/ImageReceiver_server.razor b/src/server/SmartBotBlazorApp/Components/Pages/ImageReceiver_server.razor index 0ee5718..ea22753 100644 --- a/src/server/SmartBotBlazorApp/Components/Pages/ImageReceiver_server.razor +++ b/src/server/SmartBotBlazorApp/Components/Pages/ImageReceiver_server.razor @@ -1,6 +1,8 @@ @page "/image-receiver-server" @rendermode InteractiveServer @inject NavigationManager Navigation +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject SignalHubTokenService SignalHubTokens @implements IAsyncDisposable @attribute [Authorize] @using RobotMovementInput @@ -9,12 +11,13 @@ Live View Image
-
+
@if (_imageSrc == null) { - Test Image
-
+
+ Waiting for depth image data... +
} else if (IsConnected) @@ -195,8 +198,6 @@ private static readonly int joystickCenterX = 275; private static readonly int joystickCenterY = 275; private static readonly int joystickRadius = 150; - private readonly string _testImage = debugFunctions.GetExampleImage(); - private JoystickInputHandler _joystickHandler = new JoystickInputHandler(joystickCenterX, joystickCenterY, joystickRadius); private CancellationTokenSource? _joystickCancellationTokenSource; @@ -340,15 +341,12 @@ protected override async Task OnInitializedAsync() { - //------------------------------------- DEBUG --------------------------------------------------------------- - // _measurements = debugFunctions.GenerateTestMeasurements(2); - // _avgDistance = debugFunctions.TestDistance; - // _hubUser = debugFunctions.TestUser; - // _imageSrc = debugFunctions.GetExampleImage(); - //-------------------------------------------------------------------------------------------------------- - + var authenticationState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); _hubConnection = new HubConnectionBuilder() - .WithUrl(Navigation.ToAbsoluteUri("/signalhub")) + .WithUrl( + Navigation.ToAbsoluteUri("/signalhub"), + options => options.AccessTokenProvider = () => Task.FromResult( + SignalHubTokens.CreateOperatorToken(authenticationState.User))) .Build(); _hubConnection.On("ReceiveBase64Frame", (user, measurements, base64Image, avgDistance) => diff --git a/src/server/SmartBotBlazorApp/Components/Pages/MatrixReceiver_server.razor b/src/server/SmartBotBlazorApp/Components/Pages/MatrixReceiver_server.razor index 44ad7a9..16dfbcd 100644 --- a/src/server/SmartBotBlazorApp/Components/Pages/MatrixReceiver_server.razor +++ b/src/server/SmartBotBlazorApp/Components/Pages/MatrixReceiver_server.razor @@ -2,6 +2,8 @@ @rendermode InteractiveServer @implements IAsyncDisposable @inject NavigationManager Navigation +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject SignalHubTokenService SignalHubTokens @attribute [Authorize] @using RobotMovementInput @@ -9,12 +11,13 @@ Live View Matrix
-
+
@if (_dataFrame == null) { - Test Image
-
+
+ Waiting for depth matrix data... +
} else if (IsConnected) @@ -208,8 +211,6 @@ private static readonly int joystickCenterX = 275; private static readonly int joystickCenterY = 275; private static readonly int joystickRadius = 150; - private readonly string _testImage = debugFunctions.GetExampleImage(); - private JoystickInputHandler _joystickHandler = new JoystickInputHandler(joystickCenterX, joystickCenterY, joystickRadius); private CancellationTokenSource? _joystickCancellationTokenSource; @@ -355,17 +356,13 @@ { await base.OnInitializedAsync(); //Speedometer? - //---------------------------------------DEBUG------------------------------------------------------------ - // _measurements = debugFunctions.GenerateTestMeasurements(2); - // _avgDistance = debugFunctions.TestDistance; - // _hubUser = debugFunctions.TestUser; - // _imageSrc = debugFunctions.GetExampleImage(); - // _dataFrame = debugFunctions.GenerateTestDataAscend(32); - //-------------------------------------------------------------------------------------------------------- - + var authenticationState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); _hubConnection = new HubConnectionBuilder() - .WithUrl(Navigation.ToAbsoluteUri("/signalhub")) - .Build(); + .WithUrl( + Navigation.ToAbsoluteUri("/signalhub"), + options => options.AccessTokenProvider = () => Task.FromResult( + SignalHubTokens.CreateOperatorToken(authenticationState.User))) + .Build(); _hubConnection.On("ReceiveMatrix", (user, measurements, interpolatedMatrix, avgDistance) => { diff --git a/src/server/SmartBotBlazorApp/Components/RobotMovementInput/JoystickInputHandler.cs b/src/server/SmartBotBlazorApp/Components/RobotMovementInput/JoystickInputHandler.cs index 9af9648..0fa63cf 100644 --- a/src/server/SmartBotBlazorApp/Components/RobotMovementInput/JoystickInputHandler.cs +++ b/src/server/SmartBotBlazorApp/Components/RobotMovementInput/JoystickInputHandler.cs @@ -19,7 +19,7 @@ public class JoystickInputHandler public double KnobPosX => _knobPosX; public double KnobPosY => _knobPosY; - public int LeftEngine { get; private set; } = 0 ; + public int LeftEngine { get; private set; } = 0; public int RightEngine { get; private set; } = 0; public JoystickInputHandler(double centerX, double centerY, double radius) @@ -102,7 +102,7 @@ private void SetJoystickDirection(double knobPosX, double knobPosY) validInput = true; robotDir = RobotDirectionEnum.LEFT; joystickDirection = "Left"; - + } } else // Vertical @@ -144,8 +144,8 @@ private void translateJoystickToRobotEngineValues() float deadzone = 0.35f; float stopDeadZone = 0.15f; const int maxEngineValue = 255; - - + + float tmpX = ((float)_knobPosX - (float)_centerX) / (float)_centerX; float tmpY = ((float)_knobPosY - (float)_centerY) / (float)_centerY; //odwracamy Y, bo jest ośka liczona od góry @@ -156,7 +156,7 @@ private void translateJoystickToRobotEngineValues() //DeadZone, żeby łatwiej się stopowało float leftSpeed = 0; float rightSpeed = 0; - if(Math.Abs(tmpX) < stopDeadZone && Math.Abs(tmpY) < stopDeadZone ) + if (Math.Abs(tmpX) < stopDeadZone && Math.Abs(tmpY) < stopDeadZone) { leftSpeed = 0; rightSpeed = 0; diff --git a/src/server/SmartBotBlazorApp/Components/RobotMovementInput/debugFunctions.cs b/src/server/SmartBotBlazorApp/Components/RobotMovementInput/debugFunctions.cs deleted file mode 100644 index 98a32ee..0000000 --- a/src/server/SmartBotBlazorApp/Components/RobotMovementInput/debugFunctions.cs +++ /dev/null @@ -1,80 +0,0 @@ -namespace SmartBotBlazorApp.Components.RobotMovementInput -{ - public class debugFunctions - { - public const string TestUser = "TEST_USER"; - public const ushort TestDistance = 2137; - - public static string GetExampleImage() - { - var base64PngImg = @"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"; - var image = $"data: image / png; base64,{base64PngImg}"; - - return image; - } - public static double[] GenerateTestMeasurements(int precision) - { - var random = new Random(); - var data = new double[7]; - - for (int i = 0; i < data.Length; i++) - { - data[i] = Math.Round(random.NextDouble(), precision, MidpointRounding.ToEven); ; - } - - return data; - } - public static ushort[] GenerateTestData(int resolution) - { - var random = new Random(); - ushort[] data = new ushort[resolution * resolution]; - - for (int i = 0; i < data.Length; i++) - { - data[i] = (ushort)random.Next(1, 4000); - } - - return data; - } - - public static ushort[] GenerateTestDataAscend(int resolution) - { - ushort[] data = new ushort[resolution * resolution]; - - int index = 0; - - for (int i = 0; i < resolution; i++) - { - for (int j = 0; j < resolution; j++) - { - data[index] = (ushort)(i * 125 + j); - index++; - } - } - - return data; - } - - - static public double[] GenerateTestDataAscendDouble(int resolution) - { - double[] data = new double[resolution * resolution]; - - int index = 0; - - for (int i = 0; i < resolution; i++) - { - for (int j = 0; j < resolution; j++) - { - data[index] = (ushort)(i * 125 + j); - index++; - } - } - - return data; - } - - - - } -} diff --git a/src/server/SmartBotBlazorApp/Components/RobotMovementInput/keyboardInputHandler.cs b/src/server/SmartBotBlazorApp/Components/RobotMovementInput/keyboardInputHandler.cs index b488921..aa6f333 100644 --- a/src/server/SmartBotBlazorApp/Components/RobotMovementInput/keyboardInputHandler.cs +++ b/src/server/SmartBotBlazorApp/Components/RobotMovementInput/keyboardInputHandler.cs @@ -35,7 +35,7 @@ public void onKeyDown(KeyboardEventArgs e) { counter++; pressedKey = e.Key; - + switch (e.Key) { case "ArrowUp": @@ -79,7 +79,7 @@ public void amIResetingCounter(bool reset) counter = 0; } - private void translateKeysToEngineValues() + private void translateKeysToEngineValues() { switch (robotDir) { diff --git a/src/server/SmartBotBlazorApp/Data/Measurement.cs b/src/server/SmartBotBlazorApp/Data/Measurement.cs index c53532e..6c7ad31 100644 --- a/src/server/SmartBotBlazorApp/Data/Measurement.cs +++ b/src/server/SmartBotBlazorApp/Data/Measurement.cs @@ -5,7 +5,7 @@ namespace SmartBotBlazorApp.Data { public class Measurement { - [Key] + [Key] public int Id { get; set; } [Required] @@ -15,28 +15,28 @@ public class Measurement [Required] public double TemperatureC { get; set; } - [Required] + [Required] public double AccelerationX { get; set; } [Required] public double AccelerationY { get; set; } - [Required] + [Required] public double AccelerationZ { get; set; } - [Required] + [Required] public double RotationX { get; set; } [Required] public double RotationY { get; set; } - [Required] + [Required] public double RotationZ { get; set; } - [Required] + [Required] public DateTime Timestamp { get; set; } = DateTime.Now; - [Required] + [Required] public ushort AvgDistance { get; set; } = 0; } diff --git a/src/server/SmartBotBlazorApp/Data/MeasurementService.cs b/src/server/SmartBotBlazorApp/Data/MeasurementService.cs index fe81bf6..99758d4 100644 --- a/src/server/SmartBotBlazorApp/Data/MeasurementService.cs +++ b/src/server/SmartBotBlazorApp/Data/MeasurementService.cs @@ -5,12 +5,13 @@ namespace SmartBotBlazorApp.Data public class MeasurementService { private readonly ApplicationDbContext _context; - private static DateTime _lastSaveTime = DateTime.MinValue; + private readonly RobotMeasurementThrottle _throttle; private static readonly TimeSpan SaveThrottleDuration = TimeSpan.FromSeconds(1); - public MeasurementService(ApplicationDbContext context) + public MeasurementService(ApplicationDbContext context, RobotMeasurementThrottle throttle) { _context = context; + _throttle = throttle; } public async Task AddMeasurementAsync(Measurement measurement) @@ -21,33 +22,35 @@ public async Task AddMeasurementAsync(Measurement measurement) public async Task SaveMeasurementsToDatabase(string robotId, double[] measurements, ushort distance) { - var currentTime = DateTime.Now; + ArgumentNullException.ThrowIfNull(measurements); - if ((currentTime - _lastSaveTime) >= SaveThrottleDuration) + if (measurements.Length != 7) { - var newMeasurement = new Measurement - { - RobotId = robotId, - AccelerationX = measurements[0], - AccelerationY = measurements[1], - AccelerationZ = measurements[2], - RotationX = measurements[3], - RotationY = measurements[4], - RotationZ = measurements[5], - TemperatureC = measurements[6], - AvgDistance = distance, - Timestamp = currentTime - }; - - _lastSaveTime = currentTime; - await AddMeasurementAsync(newMeasurement); + throw new ArgumentException("Seven measurement values are required.", nameof(measurements)); } - else + + if (!_throttle.TryAcquire(robotId, SaveThrottleDuration, out var timestamp)) { - await Task.CompletedTask; + return; } + + var newMeasurement = new Measurement + { + RobotId = robotId, + AccelerationX = measurements[0], + AccelerationY = measurements[1], + AccelerationZ = measurements[2], + RotationX = measurements[3], + RotationY = measurements[4], + RotationZ = measurements[5], + TemperatureC = measurements[6], + AvgDistance = distance, + Timestamp = timestamp.LocalDateTime + }; + + await AddMeasurementAsync(newMeasurement); } - + public double[] RoundMeasurements(double[] measurementsArray, int precision = 2) { var newMeasurementsArray = new double[7]; @@ -61,12 +64,12 @@ public double[] RoundMeasurements(double[] measurementsArray, int precision = 2) } public async Task> GetAllMeasurementsFromDatabaseAsync() { - return await _context.Measurements.OrderBy(m => m.Timestamp).ToListAsync(); + return await _context.Measurements.AsNoTracking().OrderBy(m => m.Timestamp).ToListAsync(); } public async Task> GetMeasurementsFromDatabaseAsync(DateTime startTimestamp, DateTime endTimestamp) { - return await _context.Measurements + return await _context.Measurements.AsNoTracking() .Where(m => m.Timestamp >= startTimestamp && m.Timestamp <= endTimestamp) .OrderBy(m => m.Timestamp) .ToListAsync(); diff --git a/src/server/SmartBotBlazorApp/Data/RobotMeasurementThrottle.cs b/src/server/SmartBotBlazorApp/Data/RobotMeasurementThrottle.cs new file mode 100644 index 0000000..74c47de --- /dev/null +++ b/src/server/SmartBotBlazorApp/Data/RobotMeasurementThrottle.cs @@ -0,0 +1,33 @@ +namespace SmartBotBlazorApp.Data; + +public sealed class RobotMeasurementThrottle(TimeProvider timeProvider) +{ + private const int MaximumTrackedRobots = 256; + private readonly Dictionary _lastSaveByRobot = new(StringComparer.Ordinal); + private readonly object _sync = new(); + + public bool TryAcquire(string robotId, TimeSpan interval, out DateTimeOffset timestamp) + { + ArgumentException.ThrowIfNullOrWhiteSpace(robotId); + + timestamp = timeProvider.GetUtcNow(); + + lock (_sync) + { + if (_lastSaveByRobot.TryGetValue(robotId, out var previousTimestamp) && + timestamp - previousTimestamp < interval) + { + return false; + } + + if (!_lastSaveByRobot.ContainsKey(robotId) && _lastSaveByRobot.Count >= MaximumTrackedRobots) + { + var oldestRobot = _lastSaveByRobot.MinBy(entry => entry.Value).Key; + _lastSaveByRobot.Remove(oldestRobot); + } + + _lastSaveByRobot[robotId] = timestamp; + return true; + } + } +} diff --git a/src/server/SmartBotBlazorApp/Dockerfile b/src/server/SmartBotBlazorApp/Dockerfile index ee87e2c..c628123 100644 --- a/src/server/SmartBotBlazorApp/Dockerfile +++ b/src/server/SmartBotBlazorApp/Dockerfile @@ -2,23 +2,26 @@ FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 8080 -EXPOSE 8081 -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:10.0.300 AS build WORKDIR /src +COPY ["global.json", "/src/global.json"] +COPY ["Directory.Build.props", "/src/Directory.Build.props"] COPY ["LICENSE", "/LICENSE"] COPY ["THIRD_PARTY_NOTICES.md", "/THIRD_PARTY_NOTICES.md"] COPY ["src/server/SmartBotBlazorApp/SmartBotBlazorApp.csproj", "SmartBotBlazorApp/"] +COPY ["src/server/SmartBotBlazorApp/packages.lock.json", "SmartBotBlazorApp/"] COPY ["src/server/SmartBotBlazorApp.Client/SmartBotBlazorApp.Client.csproj", "SmartBotBlazorApp.Client/"] +COPY ["src/server/SmartBotBlazorApp.Client/packages.lock.json", "SmartBotBlazorApp.Client/"] -RUN dotnet restore "SmartBotBlazorApp/SmartBotBlazorApp.csproj" +RUN dotnet restore "SmartBotBlazorApp/SmartBotBlazorApp.csproj" --locked-mode COPY ["src/server/", "."] -RUN dotnet build "SmartBotBlazorApp/SmartBotBlazorApp.csproj" -c Release -o /app/build FROM build AS publish -RUN dotnet publish "SmartBotBlazorApp/SmartBotBlazorApp.csproj" -c Release -o /app/publish +RUN dotnet publish "SmartBotBlazorApp/SmartBotBlazorApp.csproj" -c Release -o /app/publish --no-restore FROM base AS final WORKDIR /app COPY --from=publish /app/publish . +USER 1654 ENTRYPOINT ["dotnet", "SmartBotBlazorApp.dll"] diff --git a/src/server/SmartBotBlazorApp/Hubs/RobotMessageValidator.cs b/src/server/SmartBotBlazorApp/Hubs/RobotMessageValidator.cs index 9a1fbb7..8713dd4 100644 --- a/src/server/SmartBotBlazorApp/Hubs/RobotMessageValidator.cs +++ b/src/server/SmartBotBlazorApp/Hubs/RobotMessageValidator.cs @@ -7,6 +7,7 @@ public static class RobotMessageValidator public const int MeasurementCount = 7; public const int DepthPixelCount = 64; public const int MaximumMotorPower = 255; + public const int MaximumRobotIdLength = 50; public static void ValidateMovementCommand(int motorA, int motorB) { @@ -19,9 +20,9 @@ public static void ValidateMovementCommand(int motorA, int motorB) public static void ValidateTelemetry(string user, double[] measurements, ushort[] rawMatrix) { - if (string.IsNullOrWhiteSpace(user) || user.Length > 100) + if (string.IsNullOrWhiteSpace(user) || user.Length > MaximumRobotIdLength) { - throw new HubException("Robot identifier must contain between 1 and 100 characters."); + throw new HubException($"Robot identifier must contain between 1 and {MaximumRobotIdLength} characters."); } if (measurements is null || measurements.Length != MeasurementCount || measurements.Any(value => !double.IsFinite(value))) { diff --git a/src/server/SmartBotBlazorApp/Hubs/SignalHub.cs b/src/server/SmartBotBlazorApp/Hubs/SignalHub.cs index dd12495..6420b79 100644 --- a/src/server/SmartBotBlazorApp/Hubs/SignalHub.cs +++ b/src/server/SmartBotBlazorApp/Hubs/SignalHub.cs @@ -18,18 +18,21 @@ public SignalHub(MeasurementService measurementService, ImageProcessor imageProc [HubMethodName("SendMessage")] public async Task SendMessage(string user, string message) { + EnsureOperatorCaller(); await Clients.All.SendAsync("ReceiveMessage", user, $"Message:{message}"); } [HubMethodName("ReceiveMessage")] public async Task ReceiveMessage(string user, string message) { + EnsureOperatorCaller(); await Clients.Caller.SendAsync("ReceiveMessage", "API", "Message received!"); } [HubMethodName("SendMovementCommand")] - public async Task SendMovementCommand(string user, int motorA,int motorB) + public async Task SendMovementCommand(string user, int motorA, int motorB) { + EnsureOperatorCaller(); RobotMessageValidator.ValidateMovementCommand(motorA, motorB); await Clients.Others.SendAsync("ReceiveRobotCommand", motorB, motorA); } @@ -37,6 +40,7 @@ public async Task SendMovementCommand(string user, int motorA,int motorB) [HubMethodName("ReceiveRobotData")] public async Task ReceiveRobotData(string user, double[] measurements, ushort[] rawMatrix, ushort avgDistance) { + EnsureRobotCaller(); RobotMessageValidator.ValidateTelemetry(user, measurements, rawMatrix); var roundedMeasurements = _measurementService.RoundMeasurements(measurements, 2); @@ -46,6 +50,21 @@ await Task.WhenAll( Clients.Others.SendAsync("ReceiveMatrix", user, roundedMeasurements, _imageProcessor.InterpolateData(rawMatrix, 32), avgDistance) ); } - + + private void EnsureOperatorCaller() + { + if (!SignalHubAccess.IsOperator(Context.User)) + { + throw new HubException("This hub method requires an authenticated dashboard operator."); + } + } + + private void EnsureRobotCaller() + { + if (!SignalHubAccess.IsRobot(Context.User)) + { + throw new HubException("This hub method requires an authenticated robot connection."); + } + } } } diff --git a/src/server/SmartBotBlazorApp/ImageProcessor.cs b/src/server/SmartBotBlazorApp/ImageProcessor.cs index 5701708..5caa5b1 100644 --- a/src/server/SmartBotBlazorApp/ImageProcessor.cs +++ b/src/server/SmartBotBlazorApp/ImageProcessor.cs @@ -53,7 +53,7 @@ private static Rgba32 GetHeatmapColor(double value) { // Yellow → Green double t = (value - 0.25) / 0.25; - return new Rgba32((byte)(255 - t * 255), 255, 0); + return new Rgba32((byte)(255 - t * 255), 255, 0); } else if (value < 0.75) { @@ -64,7 +64,7 @@ private static Rgba32 GetHeatmapColor(double value) else { // Blue → Purple - double t = (value - 0.75) / 0.25; + double t = (value - 0.75) / 0.25; return new Rgba32((byte)(t * 255), 0, 255); } } diff --git a/src/server/SmartBotBlazorApp/Program.cs b/src/server/SmartBotBlazorApp/Program.cs index fb89b5e..a83b195 100644 --- a/src/server/SmartBotBlazorApp/Program.cs +++ b/src/server/SmartBotBlazorApp/Program.cs @@ -43,27 +43,21 @@ builder.Services.AddSingleton, IdentityNoOpEmailSender>(); builder.Services.AddAuthorization(); - -builder.Services.AddCors(options => -{ - options.AddPolicy("CorsPolicy", builder => - { - builder.AllowAnyOrigin() - .AllowAnyHeader() - .AllowAnyMethod(); - //.AllowCredentials(); - }); -}); +builder.Services.Configure( + builder.Configuration.GetSection(AccountAccessOptions.SectionName)); builder.Services.AddMudServices(); builder.Services.AddSignalR(); +builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddHttpClient(); builder.Services.AddScoped(); +builder.Services.AddSingleton(TimeProvider.System); +builder.Services.AddSingleton(); builder.Services.AddResponseCompression(opts => { @@ -100,8 +94,6 @@ } app.UseResponseCompression(); -app.UseCors("CorsPolicy"); - app.UseHttpsRedirection(); app.UseStaticFiles(); @@ -110,15 +102,39 @@ app.Use(async (context, next) => { - if (context.Request.Path.StartsWithSegments("/signalhub") && - !SignalHubAccess.IsAllowed( - context.User, - app.Configuration["RobotApiKey"], - context.Request.Query["access_token"].ToString())) + if (context.Request.Path.StartsWithSegments("/signalhub")) { - context.Response.StatusCode = StatusCodes.Status401Unauthorized; - await context.Response.WriteAsync("SignalR authentication required."); - return; + var suppliedRobotKey = context.Request.Query["access_token"].ToString(); + + if (context.User.Identity?.IsAuthenticated != true) + { + if (SignalHubAccess.HasValidRobotKey(app.Configuration["RobotApiKey"], suppliedRobotKey)) + { + context.User = SignalHubAccess.CreateRobotPrincipal(); + } + else + { + var authorization = context.Request.Headers.Authorization.ToString(); + var bearerToken = authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase) + ? authorization["Bearer ".Length..].Trim() + : null; + var operatorToken = string.IsNullOrEmpty(bearerToken) + ? suppliedRobotKey + : bearerToken; + var operatorPrincipal = context.RequestServices + .GetRequiredService() + .TryValidateOperatorToken(operatorToken); + + if (operatorPrincipal is null) + { + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + await context.Response.WriteAsync("SignalR authentication required."); + return; + } + + context.User = operatorPrincipal; + } + } } await next(); diff --git a/src/server/SmartBotBlazorApp/Security/SignalHubAccess.cs b/src/server/SmartBotBlazorApp/Security/SignalHubAccess.cs index 214842d..7b38535 100644 --- a/src/server/SmartBotBlazorApp/Security/SignalHubAccess.cs +++ b/src/server/SmartBotBlazorApp/Security/SignalHubAccess.cs @@ -7,14 +7,31 @@ namespace SmartBotBlazorApp; public static class SignalHubAccess { public const int MinimumApiKeyLength = 32; + public const string ClientTypeClaim = "smartbot:client_type"; + public const string OperatorClientType = "operator"; + public const string RobotClientType = "robot"; + private const string RobotAuthenticationType = "RobotApiKey"; public static bool IsAllowed(ClaimsPrincipal user, string? expectedApiKey, string? suppliedApiKey) { - if (user.Identity?.IsAuthenticated == true) - { - return true; - } + return IsOperator(user) || IsRobot(user) || HasValidRobotKey(expectedApiKey, suppliedApiKey); + } + public static bool IsOperator(ClaimsPrincipal? user) + { + return user?.Identity?.IsAuthenticated == true && !IsRobot(user); + } + + public static bool IsRobot(ClaimsPrincipal? user) + { + return user?.Identities.Any(identity => + identity.IsAuthenticated && + identity.AuthenticationType == RobotAuthenticationType && + identity.HasClaim(ClientTypeClaim, RobotClientType)) == true; + } + + public static bool HasValidRobotKey(string? expectedApiKey, string? suppliedApiKey) + { if (string.IsNullOrEmpty(expectedApiKey) || expectedApiKey.Length < MinimumApiKeyLength || string.IsNullOrEmpty(suppliedApiKey)) @@ -27,4 +44,12 @@ public static bool IsAllowed(ClaimsPrincipal user, string? expectedApiKey, strin return expectedBytes.Length == suppliedBytes.Length && CryptographicOperations.FixedTimeEquals(expectedBytes, suppliedBytes); } + + public static ClaimsPrincipal CreateRobotPrincipal() + { + var identity = new ClaimsIdentity( + [new Claim(ClientTypeClaim, RobotClientType)], + authenticationType: RobotAuthenticationType); + return new ClaimsPrincipal(identity); + } } diff --git a/src/server/SmartBotBlazorApp/Security/SignalHubTokenService.cs b/src/server/SmartBotBlazorApp/Security/SignalHubTokenService.cs new file mode 100644 index 0000000..ad52493 --- /dev/null +++ b/src/server/SmartBotBlazorApp/Security/SignalHubTokenService.cs @@ -0,0 +1,59 @@ +using System.Security.Claims; +using System.Security.Cryptography; +using Microsoft.AspNetCore.DataProtection; + +namespace SmartBotBlazorApp; + +public sealed class SignalHubTokenService +{ + private static readonly TimeSpan OperatorTokenLifetime = TimeSpan.FromMinutes(5); + private readonly ITimeLimitedDataProtector _protector; + + public SignalHubTokenService(IDataProtectionProvider dataProtectionProvider) + { + _protector = dataProtectionProvider + .CreateProtector("SmartBotAPI.SignalHub.OperatorToken.v1") + .ToTimeLimitedDataProtector(); + } + + public string CreateOperatorToken(ClaimsPrincipal user) + { + if (!SignalHubAccess.IsOperator(user)) + { + throw new InvalidOperationException("Only an authenticated dashboard user can receive an operator token."); + } + + var userId = user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.Identity?.Name; + if (string.IsNullOrWhiteSpace(userId)) + { + throw new InvalidOperationException("The authenticated dashboard user has no stable identifier."); + } + + return _protector.Protect(userId, OperatorTokenLifetime); + } + + public ClaimsPrincipal? TryValidateOperatorToken(string? token) + { + if (string.IsNullOrWhiteSpace(token)) + { + return null; + } + + try + { + var userId = _protector.Unprotect(token, out _); + var identity = new ClaimsIdentity( + [ + new Claim(ClaimTypes.NameIdentifier, userId), + new Claim(ClaimTypes.Name, userId), + new Claim(SignalHubAccess.ClientTypeClaim, SignalHubAccess.OperatorClientType) + ], + authenticationType: "SignalHubOperatorToken"); + return new ClaimsPrincipal(identity); + } + catch (CryptographicException) + { + return null; + } + } +} diff --git a/src/server/SmartBotBlazorApp/SmartBotBlazorApp.csproj b/src/server/SmartBotBlazorApp/SmartBotBlazorApp.csproj index c5ede8d..564d44d 100644 --- a/src/server/SmartBotBlazorApp/SmartBotBlazorApp.csproj +++ b/src/server/SmartBotBlazorApp/SmartBotBlazorApp.csproj @@ -6,7 +6,9 @@ enable aspnet-SmartBotBlazorApp-4a10b533-73f9-4ce1-aad8-a034de87c575 3.7 - Linux + Linux + + $(NoWarn);CS8981 @@ -18,21 +20,19 @@ - - - - - - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + - + diff --git a/src/server/SmartBotBlazorApp/appsettings.Development.json b/src/server/SmartBotBlazorApp/appsettings.Development.json index 0c208ae..21d131c 100644 --- a/src/server/SmartBotBlazorApp/appsettings.Development.json +++ b/src/server/SmartBotBlazorApp/appsettings.Development.json @@ -1,4 +1,9 @@ { + "AccountAccess": { + "AllowRegistration": true, + "ShowSelfConfirmationLink": true + }, + "Logging": { "LogLevel": { "Default": "Information", diff --git a/src/server/SmartBotBlazorApp/appsettings.json b/src/server/SmartBotBlazorApp/appsettings.json index 83534d0..633c0d3 100644 --- a/src/server/SmartBotBlazorApp/appsettings.json +++ b/src/server/SmartBotBlazorApp/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-SmartBotBlazorApp-4a10b533-73f9-4ce1-aad8-a034de87c575;Trusted_Connection=True;MultipleActiveResultSets=true" + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-SmartBotBlazorApp-4a10b533-73f9-4ce1-aad8-a034de87c575;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True" }, "Logging": { @@ -14,5 +14,10 @@ "Url": "" }, + "AccountAccess": { + "AllowRegistration": false, + "ShowSelfConfirmationLink": false + }, + "AllowedHosts": "*" } diff --git a/src/server/SmartBotBlazorApp/packages.lock.json b/src/server/SmartBotBlazorApp/packages.lock.json new file mode 100644 index 0000000..9813815 --- /dev/null +++ b/src/server/SmartBotBlazorApp/packages.lock.json @@ -0,0 +1,866 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "Microsoft.AspNetCore.Components.WebAssembly.Server": { + "type": "Direct", + "requested": "[8.0.29, )", + "resolved": "8.0.29", + "contentHash": "mF8RihuRK1PqMDDXCmD53f14Q1zp6N51UFwGLDjwiKpGEf4MWUknbRIglPIZf3Lt6c/af2pNSz1fLYXcymzNCg==" + }, + "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": { + "type": "Direct", + "requested": "[8.0.29, )", + "resolved": "8.0.29", + "contentHash": "S2oPwtyDpmRQTjPY7M/8kDqH2Eujd8FpkIav4tdgYdTM6LeM5ab+L2IklpQ+TUbtbdyItIooKHVEzbuk1pxUDg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Relational": "8.0.29" + } + }, + "Microsoft.AspNetCore.Identity.EntityFrameworkCore": { + "type": "Direct", + "requested": "[8.0.29, )", + "resolved": "8.0.29", + "contentHash": "TYM50l/64ZShlAE2jChEQapK0KPg969yYKGR8w8KGxVtktRlaNC/aN43bggoYwswiXhQ7dU2zqmMu6UH2pVbYQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Relational": "8.0.29", + "Microsoft.Extensions.Identity.Stores": "8.0.29" + } + }, + "Microsoft.AspNetCore.SignalR.Client": { + "type": "Direct", + "requested": "[8.0.29, )", + "resolved": "8.0.29", + "contentHash": "GeNFUL39i+NZ+Z8tGFLyqYpdMIwolMfrE7qUbw6ipt864yF4gEDRb/vBoOJhRRaJU6KVdXVuKwUX1s+Atvsprw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Connections.Client": "8.0.29", + "Microsoft.AspNetCore.SignalR.Client.Core": "8.0.29" + } + }, + "Microsoft.Data.SqlClient": { + "type": "Direct", + "requested": "[6.1.6, )", + "resolved": "6.1.6", + "contentHash": "6abRPXrjjEWxcNkTomBIzBdGolYnwD0ykP+6YAynaEEfKlGe94ZSQd58pggucusp28xUA7wLo9XcvzU92ZuF6Q==", + "dependencies": { + "Azure.Core": "1.50.0", + "Azure.Identity": "1.17.1", + "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Identity.Client": "4.84.2", + "Microsoft.Identity.Client.Broker": "4.84.2", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "8.0.1", + "System.Diagnostics.DiagnosticSource": "8.0.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1", + "System.Security.Cryptography.Pkcs": "8.0.1" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "Direct", + "requested": "[9.0.18, )", + "resolved": "9.0.18", + "contentHash": "ahPFWSNFE13PlCF9E7Hu2oUHydCItn4j4S71P00vV56PDKnY59nlg3gpSNTblYUvi5mwOPWtIAOud8hZOBfrQA==", + "dependencies": { + "Microsoft.Data.SqlClient": "5.1.6", + "Microsoft.EntityFrameworkCore.Relational": "9.0.18", + "Microsoft.Extensions.Caching.Memory": "9.0.18", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.18", + "Microsoft.Extensions.Logging": "9.0.18", + "System.Formats.Asn1": "9.0.18", + "System.Text.Json": "9.0.18" + } + }, + "Microsoft.EntityFrameworkCore.Tools": { + "type": "Direct", + "requested": "[9.0.18, )", + "resolved": "9.0.18", + "contentHash": "czvPv9cOVpeaSaqB4ALbQtZHNhrwGOlVhW4avMFfcLDE2bpnUF8w7EB10wQYievAHKrJpC6PoPabygnZviGfGA==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Design": "9.0.18" + } + }, + "MudBlazor": { + "type": "Direct", + "requested": "[7.16.0, )", + "resolved": "7.16.0", + "contentHash": "68bEFn7MLCyOB4yYxtvZG+AA+bZbxBDcIOhnSVFrHrAg8LLXVw1LxHCp9EyRktlREpnoAO1JxINp+5WeHc9Z4w==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.8", + "Microsoft.AspNetCore.Components.Web": "8.0.8", + "Microsoft.Extensions.Localization": "8.0.8" + } + }, + "Newtonsoft.Json": { + "type": "Direct", + "requested": "[13.0.4, )", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + }, + "SixLabors.ImageSharp": { + "type": "Direct", + "requested": "[3.1.12, )", + "resolved": "3.1.12", + "contentHash": "iAg6zifihXEFS/t7fiHhZBGAdCp3FavsF4i2ZIDp0JfeYeDVzvmlbY1CNhhIKimaIzrzSi5M/NBFcWvZT2rB/A==" + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.50.0", + "contentHash": "GBNKZEhdIbTXxedvD3R7I/yDVFX9jJJEz02kCziFSJxspSQ5RMHc3GktulJ1s7+ffXaXD7kMgrtdQTaggyInLw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.8.0", + "System.Memory.Data": "8.0.1" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.17.1", + "contentHash": "MSZkBrctcpiGxs9Cvr2VKKoN6qFLZlP3I6xuCWJ9iTgitI5Rgxtk5gfOSpXPZE3+CJmZ/mnqpQyGyjawFn5Vvg==", + "dependencies": { + "Azure.Core": "1.50.0", + "Microsoft.Identity.Client": "4.78.0", + "Microsoft.Identity.Client.Extensions.Msal": "4.78.0" + } + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Microsoft.AspNetCore.Authorization": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "IMVrMqYF7LASYMWVt/sL9f5vkA8qtfvsLEaxSchEc1Pk/zxdQt1fP1W3hN6Wxm0+wJjAKO+DhF/1mi22AtAfCQ==", + "dependencies": { + "Microsoft.AspNetCore.Metadata": "8.0.29", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.Components": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "1ZNdFYtEORG9LOYFJf0AtNMSAtkQ8WQ5XEGQXdEDvRMYBp4MS5t6mPAiWGVMb5aKpPfaeXOGHQqDcZHP3QfuAA==", + "dependencies": { + "Microsoft.AspNetCore.Authorization": "8.0.29", + "Microsoft.AspNetCore.Components.Analyzers": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.Analyzers": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "AoGUtJ3CgKhihjGCga7vNCawzWUHb7QNMtxzBJKjTYYhM9HwkGE/xmg6sZNO3Tw5W+ke5PoUO411Squ//jgiTQ==" + }, + "Microsoft.AspNetCore.Components.Authorization": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "1zf+E8B8BZod/1POB4c/YnHKPXaj/zkoFeT079iwJHWMBXxB/Y75Dug2KstusYEZBHyiHdN7UUD+Fh2edLEVvg==", + "dependencies": { + "Microsoft.AspNetCore.Authorization": "8.0.29", + "Microsoft.AspNetCore.Components": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.Forms": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "GXmEG6N30oDLZHvyxUmwKDPh/iQkcsktjUb3wffSV3Qi4DD1O37Zzz4EBIsgLQwTr4WtqjQ94yprJtKy+YSE1g==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.Web": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "1El7nDceNvO07z3Wr8uYj9J5Y7KdtIEgpbyfb9r88BYFVuZxaDDA2F4n907mWib1iLbw+XoV25XbNcPH2rne/A==", + "dependencies": { + "Microsoft.AspNetCore.Components": "8.0.29", + "Microsoft.AspNetCore.Components.Forms": "8.0.29", + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Primitives": "8.0.0", + "Microsoft.JSInterop": "8.0.29", + "System.IO.Pipelines": "8.0.0" + } + }, + "Microsoft.AspNetCore.Components.WebAssembly": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "wAPYLWm1Y2ikdUY0OgsWOJ0A1iQ0GJIvwsgbQKXTBUP/9r5rK3DvmJnZy9a0yEMslJPxvYesACNYDmLz2sTcxQ==", + "dependencies": { + "Microsoft.AspNetCore.Components.Web": "8.0.29", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.Configuration.Json": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.JSInterop.WebAssembly": "8.0.29" + } + }, + "Microsoft.AspNetCore.Components.WebAssembly.Authentication": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "rs3U4TsemB1lqnpBVtLlQbQM3ewtAImIy3OjlhM7YjxJb/1AEXmIno1Wm8S3HPScYmLZtwLrTG5//uNlYzlRag==", + "dependencies": { + "Microsoft.AspNetCore.Components.Authorization": "8.0.29", + "Microsoft.AspNetCore.Components.Web": "8.0.29" + } + }, + "Microsoft.AspNetCore.Connections.Abstractions": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "jq7oQyQE4EgWNeK6sHQn/XGyDHQ2pB5JzU4Bam9m1PQ9UtxLI/4AFqXW5Yl9VDZkTf8hkRLcJ1AbkBNCy/NiGQ==", + "dependencies": { + "Microsoft.Extensions.Features": "8.0.29", + "System.IO.Pipelines": "8.0.0" + } + }, + "Microsoft.AspNetCore.Cryptography.Internal": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "RAknfruzMmottkLWOxKzCrqOE5hp88Avr8qJU9YtxYsiJ1aH+Ij2SVcwS9Tx3VXuUesJqPADK6gZsG/bX4h6XA==" + }, + "Microsoft.AspNetCore.Cryptography.KeyDerivation": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "DIEnc7iZJHoNIar8pz+HHtm9XvshFrCSq9GtS+KLQn6AA98c9PeeoSPCwTBwvfY47KpuTUr6zNYIy/EERMzf3w==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.Internal": "8.0.29" + } + }, + "Microsoft.AspNetCore.Http.Connections.Client": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "ZnUtm+OPtOOKnnF+ZQucdPb/oBix5gyznn+XUztW7Z5c5TpWkwx4NNJVxJGoazMdpQu3EUsXF3anN2nOSK7pNg==", + "dependencies": { + "Microsoft.AspNetCore.Http.Connections.Common": "8.0.29", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.Http.Connections.Common": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "JBJI5k7nXefp2pdlk2cgdQp1zdweCcKzzeXjIalEQDuFdkJ4nMZ/Lzt3NA2XhqCKzKzKSh+EfKMytkYkxdK0NA==", + "dependencies": { + "Microsoft.AspNetCore.Connections.Abstractions": "8.0.29" + } + }, + "Microsoft.AspNetCore.Metadata": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "5Oyb4tncDGft/tXVJoyO7iWy1fnz1AzgycQfzPPSJqafmp3J+xLhgfDSTB+hzVVyG9mcb9z/8m9RDEx7bCj3Kw==" + }, + "Microsoft.AspNetCore.SignalR.Client.Core": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "T7RADZHU0G093e037prxlYIjV09mxIaVXcSip4tcpMMivawMBAR9WIJc9mqhkmAhO8RxmFKaK8BloOzLPuk/9Q==", + "dependencies": { + "Microsoft.AspNetCore.SignalR.Common": "8.0.29", + "Microsoft.AspNetCore.SignalR.Protocols.Json": "8.0.29", + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1", + "System.Threading.Channels": "8.0.0" + } + }, + "Microsoft.AspNetCore.SignalR.Common": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "silO++Rqw7ssetrLnSPTauWwJUwU/sgITKsYpuvJtDrqwZnXLYqvMflKkdS118oPLrh5GgZspvakJoQffbin4Q==", + "dependencies": { + "Microsoft.AspNetCore.Connections.Abstractions": "8.0.29", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.AspNetCore.SignalR.Protocols.Json": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "lS8j3Awf+kwG0lGHNIHXJFiCEogdTroLhmRxQ4+T68aJUaXIu0LxVAZL9hjm16LoohL4jVmTPwgQHF7mYKo4Ow==", + "dependencies": { + "Microsoft.AspNetCore.SignalR.Common": "8.0.29" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Build.Framework": { + "type": "Transitive", + "resolved": "17.8.43", + "contentHash": "TA6fCbd9qFSyhIKcUn5BhDN3/JrqpspO9KYsZKUvHyQpQrHVdzy4Hk6egjjBP3T0L562TLZuHEzQwJziyRBEkw==" + }, + "Microsoft.Build.Locator": { + "type": "Transitive", + "resolved": "1.7.8", + "contentHash": "sPy10x527Ph16S2u0yGME4S6ohBKJ69WfjeGG/bvELYeZVmJdKjxgnlL8cJJJLGV/cZIRqSfB12UDB8ICakOog==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.4", + "contentHash": "AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "/jR+e/9aT+BApoQJABlVCKnnggGQbvGh7BKq2/wI1LamxC+LbzhcLj4Vj7gXCofl1n4E521YfF9w0WcASGg/KA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.4", + "System.Collections.Immutable": "7.0.0", + "System.Reflection.Metadata": "7.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "+3+qfdb/aaGD8PZRCrsdobbzGs1m9u119SkkJt8e/mk3xLJz/udLtS2T6nY27OTXxBBw10HzAbC8Z9w08VyP/g==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.8.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "3amm4tq4Lo8/BGvg9p3BJh3S9nKq2wqCXfS7138i69TUpo/bD+XvD0hNurpEBtcNZhi1FyutiomKJqVF39ugYA==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.8.0]", + "Microsoft.CodeAnalysis.Common": "[4.8.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.8.0]" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "LXyV+MJKsKRu3FGJA3OmSk40OUIa/dQCFLOnm5X8MNcujx7hzGu8o+zjXlb/cy5xUdZK2UKYb9YaQ2E8m9QehQ==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", + "Microsoft.CodeAnalysis.Common": "[4.8.0]", + "System.Composition": "7.0.0", + "System.IO.Pipelines": "7.0.0", + "System.Threading.Channels": "7.0.0" + } + }, + "Microsoft.CodeAnalysis.Workspaces.MSBuild": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "IEYreI82QZKklp54yPHxZNG9EKSK6nHEkeuf+0Asie9llgS1gp0V1hw7ODG+QyoB7MuAnNQHmeV1Per/ECpv6A==", + "dependencies": { + "Microsoft.Build.Framework": "16.10.0", + "Microsoft.CodeAnalysis.Common": "[4.8.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.8.0]", + "System.Text.Json": "7.0.3" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "ByxJvHZwP1QWLXmPLNClJ4mVAgzAjzdKlK/pb2E9qbyuQdhwGTy/yfTDsPlwYtXlwnDes9/2p4mOP/+Cbo4pFA==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "9.0.18", + "Microsoft.EntityFrameworkCore.Analyzers": "9.0.18", + "Microsoft.Extensions.Caching.Memory": "9.0.18", + "Microsoft.Extensions.Logging": "9.0.18" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "4nD2ZtEG/fNyHT4bo0MovOw6wDVv2xunhmRm448AKbX3y2ohEZkkxI4Nm+JHBZmluzsV6HHWFRWawFj8VuNltg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "KI+qKZHU1OfEUjQEpQEh/EXo9wN8DP+Jt1aReNJe/MezR/cVTr79c4VeMv4cyizki7YWW3DDDWZCcy/Eiay6dg==" + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "2Uxb8lOn29yaFZIXBF2m9lrziYpxnSB+KSegj/4xqjLWW/AdGIdz4AYPajgSY9DPUI5AP1WTO4EwjP5LorukxA==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Build.Framework": "17.8.43", + "Microsoft.Build.Locator": "1.7.8", + "Microsoft.CodeAnalysis.CSharp": "4.8.0", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.8.0", + "Microsoft.CodeAnalysis.Workspaces.MSBuild": "4.8.0", + "Microsoft.EntityFrameworkCore.Relational": "9.0.18", + "Microsoft.Extensions.Caching.Memory": "9.0.18", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.18", + "Microsoft.Extensions.DependencyModel": "9.0.18", + "Microsoft.Extensions.Logging": "9.0.18", + "Mono.TextTemplating": "3.0.0", + "System.Text.Json": "9.0.18" + } + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "kn6DongUAVevGsydWJdJUB0N6QamtwyDjhwrbV8Yrfs84i5rcEj5SY+3eyPzAInzd1icrZBd7A+r0yEpULkjEg==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "9.0.18", + "Microsoft.Extensions.Caching.Memory": "9.0.18", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.18", + "Microsoft.Extensions.Logging": "9.0.18" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "Y8PnPKq+ASBazn0QA5d98/dWT0MAfv9CJYV6zPZ/TisZpAUe5zWgKldJLXkrmUuIzvUpQS7/SF7+1dld3OP/EQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.18" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "ZQ+x93CRR8DOGT+YPP4YXvwTAJ0MfvAghs5cwCSvtZFiORSfprAw9f67d0QDhf6PCIYyFsMKFB+PI3p9lpiMUQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "9.0.18", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.18", + "Microsoft.Extensions.Logging.Abstractions": "9.0.18", + "Microsoft.Extensions.Options": "9.0.18", + "Microsoft.Extensions.Primitives": "9.0.18" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "jgC5SZeK94t8DXl836ifIM5gScNqknkOwISrY503eWg7AHBrv+fnwty+C9n35H4WvBjs8OY21jPlRRf7ESBK/g==", + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.18" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "xPoYhpBypsweLpZ29GPp6Kv6g1fZMUwJ800D/xO7ASuNn5zx0BNIP600wXXhbPNeO8kKngFRtoWplRffxBUDgw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.18" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "eSUdaLUP+pj1J7SpCsUYQP6dNk0w9g0KGu4ovPYBnlfqomW1u+1fF8ZIjMVuHY6Z3p30yL0SxND9JTS6i3NolQ==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "uv+aDeo/9tDFxb5ykdU9WTPYDOzeFK53m51QbYHaWe7IvPRMSyMDUlDDXSRYJ2aPNWAE5ISPjvvHQL9UlQS+vw==", + "dependencies": { + "System.Text.Encodings.Web": "9.0.18", + "System.Text.Json": "9.0.18" + } + }, + "Microsoft.Extensions.Features": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "0zMsmVIfuu9AP3pgIbHt3SOdEHHdGZSbRlTl8+ysZkNLbbZ0B2tm0Y6kYfuw/VsuAuKHy0IOOgobm3maYRQCIg==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Identity.Core": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "os0Bq475TseOIY+ieTPW/0Iyoe9B+qEr+ahipj9ojpvPgA95tRZXYGaEHSvjv71/AqRgFdN+gSi3AExT3wih7g==", + "dependencies": { + "Microsoft.AspNetCore.Cryptography.KeyDerivation": "8.0.29", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Identity.Stores": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "AbZ/yMoGstUaDucifJ25/Dm344GuQv8LYLrIsPTkGZ/H+Nb1/oyfJXV+acQ3Gon6uNqvXso4W/xqqiSTkUqsvQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.Identity.Core": "8.0.29", + "Microsoft.Extensions.Logging": "8.0.1" + } + }, + "Microsoft.Extensions.Localization": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "n32ZGmc2UOv9R/xsBHQPOxBsqCSAAJSoMtFqoQ8zOegB2P+H6+773OyCWg4jRBkO/3dmCyBJXB0yLAOVW2/C/w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.1", + "Microsoft.Extensions.Localization.Abstractions": "8.0.8", + "Microsoft.Extensions.Logging.Abstractions": "8.0.1", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Localization.Abstractions": { + "type": "Transitive", + "resolved": "8.0.8", + "contentHash": "WYIsqP/A6dH/LWJznrvgoNPc7Q+CUJD0E78765GL3aonBtyFK1BKtPzBvvlnrr3SVqSO2r6xJCLgCEiCMG1gfA==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "LuOCZhcJ4TeCLE99TRqI1FHReB29G7bAPInrAAG3E85/S+E4mcrmaoeoXLnOUi6DEosx87pauyFNICdiFIFqkA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "9.0.18", + "Microsoft.Extensions.Logging.Abstractions": "9.0.18", + "Microsoft.Extensions.Options": "9.0.18" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "uzNHNdNZJTHACwFkWclx8s5GXLjGacJj4QwBQZ/6BeixTJjlHmNESIK738iPFLPkO8jWsENLUcUW/kAPe0b8ew==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.18", + "System.Diagnostics.DiagnosticSource": "9.0.18" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "HiA/R0jGR10TGuR6coM8KMUdJ5YlP3bhGxqEdGLkxFdBV1EFNdZMAajSEZHwkex4X/iMQwRCQVotqpElcxOImg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.18", + "Microsoft.Extensions.Primitives": "9.0.18" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "hfHudMC5zDlwMrC0HiHOJesSHMvM+CdqjomjcV/YVzFq5dfSpBRvyRLm1n1Bfh41ZpQnyJzqX+YEo95BAmcDAQ==" + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.84.2", + "contentHash": "Va9FjmABSgj/lcUfHH0pBNQkmS7SNyepz2ERV7Yynp6QgEYpFdSqR6Vzy1WWipN8GS5pBHuXoZbqaDWuARCrHQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "8.14.0", + "System.Diagnostics.DiagnosticSource": "6.0.1" + } + }, + "Microsoft.Identity.Client.Broker": { + "type": "Transitive", + "resolved": "4.84.2", + "contentHash": "928ySLeGz1xtvydwq6h9tv7TbxrtA4ZzavLKUqRJh0PW6BGcEJwpI8W8vf2PlQdYemGm/oyGjdzYzdY/I8r/4A==", + "dependencies": { + "Microsoft.Identity.Client": "4.84.2", + "Microsoft.Identity.Client.NativeInterop": "0.20.6" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.78.0", + "contentHash": "DYU9o+DrDQuyZxeq91GBA9eNqBvA3ZMkLzQpF7L9dTk6FcIBM1y1IHXWqiKXTvptPF7CZE59upbyUoa+FJ5eiA==", + "dependencies": { + "Microsoft.Identity.Client": "4.78.0", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.Identity.Client.NativeInterop": { + "type": "Transitive", + "resolved": "0.20.6", + "contentHash": "noyfdMfVxyWpM6xUDmlW6gUvZ5ci24z7Qoy15JvynTQfZV5vTD2NEdsnT5Gy4ngViwgdIp5iRjRy10jAKXFAgw==" + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "8.14.0", + "contentHash": "iwbCpSjD3ehfTwBhtSNEtKPK0ICun6ov7Ibx6ISNA9bfwIyzI2Siwyi9eJFCJBwxowK9xcA1mj+jBWiigeqgcQ==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.7.1" + } + }, + "Microsoft.JSInterop": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "6N3e6a5YHcV53/jU63XBvLEOEV3ctWSFZi+hNl0HNRHYd3aJrTSA9Wl6tq7/9piO+pYRat4V6wPB2NEWMe3TnA==" + }, + "Microsoft.JSInterop.WebAssembly": { + "type": "Transitive", + "resolved": "8.0.29", + "contentHash": "aPT2wbEYTKwFYwSGGNEoX0af5jrAPIG8zzMuZ02Hfh4+6+0lFAdRcZ3GEHljoVbE491WSLgoeyMQTcUKx3zYTQ==", + "dependencies": { + "Microsoft.JSInterop": "8.0.29" + } + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "Mono.TextTemplating": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "YqueG52R/Xej4VVbKuRIodjiAhV0HR/XVbLbNrJhCZnzjnSjgMJ/dCdV0akQQxavX6hp/LC6rqLGLcXeQYU7XA==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "System.ClientModel": { + "type": "Transitive", + "resolved": "1.8.0", + "contentHash": "AqRzhn0v29GGGLj/Z6gKq4lGNtvPHT4nHdG5PDJh9IfVjv/nYUVmX11hwwws1vDFeIAzrvmn0dPu8IjLtu6fAw==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "System.Memory.Data": "8.0.1" + } + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==" + }, + "System.Composition": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "tRwgcAkDd85O8Aq6zHDANzQaq380cek9lbMg5Qma46u5BZXq/G+XvIYmu+UI+BIIZ9zssXLYrkTykEqxxvhcmg==", + "dependencies": { + "System.Composition.AttributedModel": "7.0.0", + "System.Composition.Convention": "7.0.0", + "System.Composition.Hosting": "7.0.0", + "System.Composition.Runtime": "7.0.0", + "System.Composition.TypedParts": "7.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "2QzClqjElKxgI1jK1Jztnq44/8DmSuTSGGahXqQ4TdEV0h9s2KikQZIgcEqVzR7OuWDFPGLHIprBJGQEPr8fAQ==" + }, + "System.Composition.Convention": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "IMhTlpCs4HmlD8B+J8/kWfwX7vrBBOs6xyjSTzBlYSs7W4OET4tlkR/Sg9NG8jkdJH9Mymq0qGdYS1VPqRTBnQ==", + "dependencies": { + "System.Composition.AttributedModel": "7.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "eB6gwN9S+54jCTBJ5bpwMOVerKeUfGGTYCzz3QgDr1P55Gg/Wb27ShfPIhLMjmZ3MoAKu8uUSv6fcCdYJTN7Bg==", + "dependencies": { + "System.Composition.Runtime": "7.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "aZJ1Zr5Txe925rbo4742XifEyW0MIni1eiUebmcrP3HwLXZ3IbXUj4MFMUH/RmnJOAQiS401leg/2Sz1MkApDw==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "ZK0KNPfbtxVceTwh+oHNGUOYV2WNOHReX2AXipuvkURC7s/jPwoWfsu3SnDBDgofqbiWr96geofdQ2erm/KTHg==", + "dependencies": { + "System.Composition.AttributedModel": "7.0.0", + "System.Composition.Hosting": "7.0.0", + "System.Composition.Runtime": "7.0.0" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "gPYFPDyohW2gXNhdQRSjtmeS6FymL2crg4Sral1wtvEJ7DUqFCDWDVbbLobASbzxfic8U1hQEdC7hmg9LHncMw==", + "dependencies": { + "System.Diagnostics.EventLog": "8.0.1", + "System.Security.Cryptography.ProtectedData": "8.0.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "zXv17I0lirlmcrLyvSYUHYpXDh/pJ54QfKI96/132KEMsgaG2m61UisI9CJrayZq1mRQL1u4T2ri6eVM6auhPg==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "n1ZP7NM2Gkn/MgD8+eOT5MulMj6wfeQMNS2Pizvq5GHCZfjlFMXV2irQlQmJhwA2VABC57M0auudO89Iu2uRLg==" + }, + "System.Formats.Asn1": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "FHZkK31ffB8aVfqhh24E2p96Ezihj2d2kE8Ksf6hcPb0uP1kRW9XTAFHa5Y3Ph4kKCdl2TGH8dCr/cHG7TDK6g==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "9AcBFLF9KZpCiv+xTN8emSsiF1FEq/DdswZrJgqlf37dKThJSXwCQ1QUfH24+k9X89C80CI0IQCZatdg0yj3Jw==" + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "MclTG61lsD9sYdpNz9xsKBzjsmsfCtcMZYXz/IUr2zlhaTaABonlr1ESeompTgM+Xk+IwtGYU7/voh3YWB/fWw==", + "dependencies": { + "System.Collections.Immutable": "7.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "CoCRHFym33aUSf/NtWSVSZa99dkd0Hm7OCZUxORBjRB16LNhIEOf8THPqzIYlvKM0nNDAPTRBa1FxEECrgaxxA==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "Uqybp2r2zFvSDKckcr30TbXRrjW16ihRFzdHVC8Jo8DwkRDJfBuPkXS7hqcUwRYE3ItHKeyfcbq/SpExNPuE2Q==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "9.0.18", + "contentHash": "/xX8vajlGUp1FoGSNUJBJS+E/2TQjmcMoUzHePa+coPdLnFGPcolRHlybSZhKyAKj+sNDSFlLNYYLe8aEOElfA==", + "dependencies": { + "System.IO.Pipelines": "9.0.18", + "System.Text.Encodings.Web": "9.0.18" + } + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==" + }, + "smartbotblazorapp.client": { + "type": "Project", + "dependencies": { + "Microsoft.AspNetCore.Components.WebAssembly": "[8.0.29, )", + "Microsoft.AspNetCore.Components.WebAssembly.Authentication": "[8.0.29, )", + "Microsoft.AspNetCore.SignalR.Client": "[8.0.29, )" + } + } + } + } +} \ No newline at end of file diff --git a/src/server/SmartBotBlazorApp/wwwroot/favicon.png b/src/server/SmartBotBlazorApp/wwwroot/favicon.png index d9923a4..908cc7b 100644 Binary files a/src/server/SmartBotBlazorApp/wwwroot/favicon.png and b/src/server/SmartBotBlazorApp/wwwroot/favicon.png differ