From 5e830ad0eeb86b37c8de3e3a322ba86388c65b37 Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Thu, 12 Oct 2023 07:51:09 +0200 Subject: [PATCH 1/7] Add page for uploading json files from tenor to configure persons and orgs Now you can open a separate tab on the localtest site and upload tenor kildedata to localtest internal storage. If there are relevant tenor files in storage, those will replace Ola Nordmann and Sofie Salt as the localtest users. You can also select a subset of the files to downoad testData.json to be used as app local users. In addtition to the user management page, I have included the following changes. * Cleanup of login page and use the menu on top instead of links scattered all over the login screen * Use latest bootstrap (from CDN, per getboostrap.com instructions) * Split HomeController into multiple smaller controllers. * Move `Reauthenticate` button besides the login button and make it a less visible color. * Loadbalancer redirects "/" and /Home/* to localtest, so all new controllers got a `/Home/[controller]/[action]` prefix * use only spaces (not mixed with tabs) in nginx.conf.conf --- loadbalancer/templates/nginx.conf.conf | 40 +- src/Configuration/LocalPlatformSettings.cs | 2 + src/Controllers/DebugUsersController.cs | 74 ++++ src/Controllers/FrontendVersionController.cs | 103 +++++ src/Controllers/HomeController.cs | 108 +----- src/Controllers/StorageExplorerController.cs | 39 ++ src/Controllers/TenorUsersController.cs | 86 +++++ src/Models/Authentication/CustomClaim.cs | 7 +- src/Models/Authorization/Role.cs | 6 +- src/Models/TenorViewModel.cs | 19 + src/Services/Tenor/Models/BrregErFr.cs | 365 ++++++++++++++++++ src/Services/Tenor/Models/Freg.cs | 342 ++++++++++++++++ src/Services/Tenor/TenorDataReader.cs | 198 ++++++++++ src/Services/TestData/AppTestDataModel.cs | 55 ++- src/Services/TestData/TestDataService.cs | 16 +- src/Startup.cs | 1 + .../Index.cshtml} | 6 +- src/Views/Home/Index.cshtml | 20 +- src/Views/Shared/_Layout.cshtml | 28 +- src/Views/StorageExplorer/Index.cshtml | 9 + src/Views/TenorUsers/Index.cshtml | 98 +++++ 21 files changed, 1468 insertions(+), 154 deletions(-) create mode 100644 src/Controllers/DebugUsersController.cs create mode 100644 src/Controllers/FrontendVersionController.cs create mode 100644 src/Controllers/StorageExplorerController.cs create mode 100644 src/Controllers/TenorUsersController.cs create mode 100644 src/Models/TenorViewModel.cs create mode 100644 src/Services/Tenor/Models/BrregErFr.cs create mode 100644 src/Services/Tenor/Models/Freg.cs create mode 100644 src/Services/Tenor/TenorDataReader.cs rename src/Views/{Home/FrontendVersion.cshtml => FrontendVersion/Index.cshtml} (66%) create mode 100644 src/Views/StorageExplorer/Index.cshtml create mode 100644 src/Views/TenorUsers/Index.cshtml diff --git a/loadbalancer/templates/nginx.conf.conf b/loadbalancer/templates/nginx.conf.conf index 451c463d..0eb25a28 100644 --- a/loadbalancer/templates/nginx.conf.conf +++ b/loadbalancer/templates/nginx.conf.conf @@ -31,7 +31,7 @@ http { sendfile on; - upstream localtest { + upstream localtest { server host.docker.internal:5101; } @@ -58,7 +58,7 @@ http { } server { - listen 80 default_server; + listen 80 default_server; server_name ${TEST_DOMAIN}; proxy_redirect off; @@ -68,50 +68,50 @@ http { error_page 502 /502LocalTest.html; - location = / { + location = / { proxy_pass http://localtest/Home/; sub_filter '' ''; - } + } - location / { + location / { #Support using Local js, when a cookie value is set sub_filter_once off; sub_filter 'https://altinncdn.no/toolkits/altinn-app-frontend/3/' $LOCAL_SUB_FILTER; proxy_pass http://app/; error_page 502 /502App.html; proxy_cookie_domain altinn3local.no local.altinn.cloud; - } + } location /Home/_framework/ { - proxy_pass http://localtest/_framework/; + proxy_pass http://localtest/_framework/; } - location /Home/ { - proxy_pass http://localtest/Home/; + location /Home/ { + proxy_pass http://localtest/Home/; sub_filter '' ''; - } + } location /receipt/ { - proxy_pass http://receiptcomp/receipt/; - error_page 502 /502Receipt.html; - } + proxy_pass http://receiptcomp/receipt/; + error_page 502 /502Receipt.html; + } location /accessmanagement/ { - proxy_pass http://accessmanagementcomp/accessmanagement/; + proxy_pass http://accessmanagementcomp/accessmanagement/; error_page 502 /502Accessmanagement.html; - } + } location /storage/ { - proxy_pass http://localtest/storage/; - } + proxy_pass http://localtest/storage/; + } location /pdfservice/ { proxy_pass http://pdfservice/; } location /localtestresources/ { - proxy_pass http://localtest/localtestresources/; - } + proxy_pass http://localtest/localtestresources/; + } location /LocalPlatformStorage/ { proxy_pass http://localtest/LocalPlatformStorage/; } @@ -128,5 +128,5 @@ http { root /www; } - } + } } diff --git a/src/Configuration/LocalPlatformSettings.cs b/src/Configuration/LocalPlatformSettings.cs index 6f5c5231..8b3aadcb 100644 --- a/src/Configuration/LocalPlatformSettings.cs +++ b/src/Configuration/LocalPlatformSettings.cs @@ -68,5 +68,7 @@ public string LocalTestingStaticTestDataPath { public string RolesFolder { get; set; } = "roles/"; public string ClaimsFolder { get; set; } = "claims/"; + + public string TenorDataFolder { get; set; } = "tenorUsers"; } } diff --git a/src/Controllers/DebugUsersController.cs b/src/Controllers/DebugUsersController.cs new file mode 100644 index 00000000..6dfa0289 --- /dev/null +++ b/src/Controllers/DebugUsersController.cs @@ -0,0 +1,74 @@ +#nullable enable +using System.Text.Json; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +using Altinn.Platform.Storage.Repository; + +using LocalTest.Configuration; +using LocalTest.Models; +using LocalTest.Services.LocalApp.Interface; + +using LocalTest.Services.TestData; +using Microsoft.AspNetCore.Authorization; + +namespace LocalTest.Controllers; + +[Route("Home/[controller]/[action]")] +public class DebugUsersController : Controller +{ + private readonly TenorDataRepository _tenorDataRepository; + private readonly LocalPlatformSettings _localPlatformSettings; + private readonly ILocalApp _localApp; + + public DebugUsersController( + TenorDataRepository tenorDataRepository, + IOptions localPlatformSettings, + ILocalApp localApp) + { + _tenorDataRepository = tenorDataRepository; + _localPlatformSettings = localPlatformSettings.Value; + _localApp = localApp; + } + + // Debugging endpoint + [AllowAnonymous] + public async Task LocalTestUsersRaw() + { + var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); + + return Json(localData); + } + + //Debugging endpoint + [AllowAnonymous] + public async Task LocalTestUsers() + { + var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); + var constructedAppData = AppTestDataModel.FromTestDataModel(localData); + + return Json(constructedAppData); + } + + // Debugging endpoint + [AllowAnonymous] + public async Task LocalTestUsersRoundTrip() + { + var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); + var constructedAppData = AppTestDataModel.FromTestDataModel(localData); + + return Json(constructedAppData.GetTestDataModel()); + } + + // Debugging endpoint + [AllowAnonymous] + public async Task ShowTenorUsers([FromServices] TenorDataRepository tenorDataRepository) + { + var localData = await tenorDataRepository.GetAppTestDataModel(); + + return Json(localData); + } + + +} \ No newline at end of file diff --git a/src/Controllers/FrontendVersionController.cs b/src/Controllers/FrontendVersionController.cs new file mode 100644 index 00000000..f22e496f --- /dev/null +++ b/src/Controllers/FrontendVersionController.cs @@ -0,0 +1,103 @@ +#nullable enable +using System.Text.Json; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +using Altinn.Platform.Storage.Repository; + +using LocalTest.Configuration; +using LocalTest.Models; +using LocalTest.Services.LocalApp.Interface; + +using LocalTest.Services.TestData; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace LocalTest.Controllers; + +[Route("Home/[controller]/[action]")] +public class FrontendVersionController : Controller +{ + private readonly TenorDataRepository _tenorDataRepository; + private readonly LocalPlatformSettings _localPlatformSettings; + private readonly ILocalApp _localApp; + + public FrontendVersionController( + TenorDataRepository tenorDataRepository, + IOptions localPlatformSettings, + ILocalApp localApp) + { + _tenorDataRepository = tenorDataRepository; + _localPlatformSettings = localPlatformSettings.Value; + _localApp = localApp; + } + /// + /// See src\development\loadbalancer\nginx.conf + /// + public static readonly string FRONTEND_URL_COOKIE_NAME = "frontendVersion"; + + [HttpGet] + public async Task Index([FromServices] HttpClient client) + { + var versionFromCookie = HttpContext.Request.Cookies[FRONTEND_URL_COOKIE_NAME]; + + var frontendVersion = new FrontendVersion() + { + Version = versionFromCookie, + Versions = new List() + { + new () + { + Text = "Keep as is", + Value = "", + }, + new () + { + Text = "localhost:8080 (local dev)", + Value = "http://localhost:8080/" + } + } + }; + var cdnVersionsString = await client.GetStringAsync("https://altinncdn.no/toolkits/altinn-app-frontend/index.json"); + var groupCdnVersions = new SelectListGroup() { Name = "Specific version from cdn" }; + var versions = JsonSerializer.Deserialize>(cdnVersionsString)!; + versions.Reverse(); + versions.ForEach(version => + { + frontendVersion.Versions.Add(new() + { + Text = version, + Value = $"https://altinncdn.no/toolkits/altinn-app-frontend/{version}/", + Group = groupCdnVersions + }); + }); + + return View(frontendVersion); + } + public ActionResult Index(FrontendVersion frontendVersion) + { + var options = new CookieOptions + { + Expires = DateTime.MaxValue, + HttpOnly = true, + }; + ICookieManager cookieManager = new ChunkingCookieManager(); + if (string.IsNullOrWhiteSpace(frontendVersion.Version)) + { + cookieManager.DeleteCookie(HttpContext, FRONTEND_URL_COOKIE_NAME, options); + } + else + { + cookieManager.AppendResponseCookie( + HttpContext, + FRONTEND_URL_COOKIE_NAME, + frontendVersion.Version, + options + ); + } + + return RedirectToAction("Index", "Home"); + } +} \ No newline at end of file diff --git a/src/Controllers/HomeController.cs b/src/Controllers/HomeController.cs index 0fa01b82..2a2f202d 100644 --- a/src/Controllers/HomeController.cs +++ b/src/Controllers/HomeController.cs @@ -24,6 +24,7 @@ namespace LocalTest.Controllers { + [Route("/Home/[action]")] public class HomeController : Controller { private readonly GeneralSettings _generalSettings; @@ -32,7 +33,7 @@ public class HomeController : Controller private readonly IAuthentication _authenticationService; private readonly IApplicationRepository _applicationRepository; private readonly IParties _partiesService; - private readonly IClaims _claimsService; + private readonly ILocalApp _localApp; private readonly TestDataService _testDataService; @@ -43,7 +44,6 @@ public HomeController( IAuthentication authenticationService, IApplicationRepository applicationRepository, IParties partiesService, - IClaims claimsService, ILocalApp localApp, TestDataService testDataService) { @@ -53,40 +53,14 @@ public HomeController( _authenticationService = authenticationService; _applicationRepository = applicationRepository; _partiesService = partiesService; - _claimsService = claimsService; _localApp = localApp; _testDataService = testDataService; } [AllowAnonymous] - public async Task LocalTestUsersRaw() - { - var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); - - return Json(localData); - } - - //Debugging endpoint - [AllowAnonymous] - public async Task LocalTestUsers() - { - var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); - var constructedAppData = AppTestDataModel.FromTestDataModel(localData); - - return Json(constructedAppData); - } - - // Debugging endpoint - [AllowAnonymous] - public async Task LocalTestUsersRoundTrip() - { - var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); - var constructedAppData = AppTestDataModel.FromTestDataModel(localData); - - return Json(constructedAppData.GetTestDataModel()); - } - - [AllowAnonymous] + [HttpGet("/")] + [HttpGet("/Home")] + [HttpGet("/Home/Index")] public async Task Index() { StartAppModel model = new StartAppModel() @@ -95,7 +69,7 @@ public async Task Index() AppPath = _localPlatformSettings.AppRepositoryBasePath, StaticTestDataPath = _localPlatformSettings.LocalTestingStaticTestDataPath, LocalAppUrl = _localPlatformSettings.LocalAppUrl, - LocalFrontendUrl = HttpContext.Request.Cookies[FRONTEND_URL_COOKIE_NAME], + LocalFrontendUrl = HttpContext.Request.Cookies[FrontendVersionController.FRONTEND_URL_COOKIE_NAME], }; try @@ -234,76 +208,6 @@ public async Task GetTestOrgToken(string id, [FromQuery] string or return Ok(token); } - /// - /// See src\development\loadbalancer\nginx.conf - /// - public static readonly string FRONTEND_URL_COOKIE_NAME = "frontendVersion"; - - [HttpGet] - public async Task FrontendVersion([FromServices] HttpClient client) - { - var versionFromCookie = HttpContext.Request.Cookies[FRONTEND_URL_COOKIE_NAME]; - - - - var frontendVersion = new FrontendVersion() - { - Version = versionFromCookie, - Versions = new List() - { - new () - { - Text = "Keep as is", - Value = "", - }, - new () - { - Text = "localhost:8080 (local dev)", - Value = "http://localhost:8080/" - } - } - }; - var cdnVersionsString = await client.GetStringAsync("https://altinncdn.no/toolkits/altinn-app-frontend/index.json"); - var groupCdnVersions = new SelectListGroup() { Name = "Specific version from cdn" }; - var versions = JsonSerializer.Deserialize>(cdnVersionsString); - versions.Reverse(); - versions.ForEach(version => - { - frontendVersion.Versions.Add(new() - { - Text = version, - Value = $"https://altinncdn.no/toolkits/altinn-app-frontend/{version}/", - Group = groupCdnVersions - }); - }); - - return View(frontendVersion); - } - public ActionResult FrontendVersion(FrontendVersion frontendVersion) - { - var options = new CookieOptions - { - Expires = DateTime.MaxValue, - HttpOnly = true, - }; - ICookieManager cookieManager = new ChunkingCookieManager(); - if (string.IsNullOrWhiteSpace(frontendVersion.Version)) - { - cookieManager.DeleteCookie(HttpContext, FRONTEND_URL_COOKIE_NAME, options); - } - else - { - cookieManager.AppendResponseCookie( - HttpContext, - FRONTEND_URL_COOKIE_NAME, - frontendVersion.Version, - options - ); - } - - return RedirectToAction("Index"); - } - private async Task> GetTestUsersForList() { var data = await _testDataService.GetTestData(); diff --git a/src/Controllers/StorageExplorerController.cs b/src/Controllers/StorageExplorerController.cs new file mode 100644 index 00000000..3970ec0c --- /dev/null +++ b/src/Controllers/StorageExplorerController.cs @@ -0,0 +1,39 @@ +#nullable enable +using System.Text.Json; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +using Altinn.Platform.Storage.Repository; + +using LocalTest.Configuration; +using LocalTest.Models; +using LocalTest.Services.LocalApp.Interface; + +using LocalTest.Services.TestData; +using Microsoft.AspNetCore.Authorization; + +namespace LocalTest.Controllers; + +[Route("Home/[controller]/[action]")] +public class StorageExplorerController : Controller +{ + private readonly TenorDataRepository _tenorDataRepository; + private readonly LocalPlatformSettings _localPlatformSettings; + private readonly ILocalApp _localApp; + + public StorageExplorerController( + TenorDataRepository tenorDataRepository, + IOptions localPlatformSettings, + ILocalApp localApp) + { + _tenorDataRepository = tenorDataRepository; + _localPlatformSettings = localPlatformSettings.Value; + _localApp = localApp; + } + + public IActionResult Index() + { + return View(); + } +} \ No newline at end of file diff --git a/src/Controllers/TenorUsersController.cs b/src/Controllers/TenorUsersController.cs new file mode 100644 index 00000000..76492eef --- /dev/null +++ b/src/Controllers/TenorUsersController.cs @@ -0,0 +1,86 @@ +#nullable enable +using System.Text.Json; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +using Altinn.Platform.Storage.Repository; + +using LocalTest.Configuration; +using LocalTest.Models; +using LocalTest.Services.LocalApp.Interface; + +using LocalTest.Services.TestData; + +namespace LocalTest.Controllers; + +[Route("Home/[controller]/[action]")] +public class TenorUsersController : Controller +{ + private readonly TenorDataRepository _tenorDataRepository; + private readonly ILocalApp _localApp; + + public TenorUsersController( + TenorDataRepository tenorDataRepository, + ILocalApp localApp) + { + _tenorDataRepository = tenorDataRepository; + _localApp = localApp; + } + + + + public async Task Index() + { + var appUsers = await _localApp.GetTestData(); + + return View(new TenorViewModel() + { + AppUsers = appUsers, + FileItems = await _tenorDataRepository.GetFileItems(), + + }); + } + + + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Upload() + { + //TODO: validate uploaded files + foreach (var file in Request.Form.Files) + { + await _tenorDataRepository.StoreUploadedFile(file); + } + return RedirectToAction("Index"); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Update() + { + var files = Request.Form.Keys.Where(k => k.EndsWith(".json")).ToList(); + if (Request.Form.ContainsKey("Download")) + { + return Json(await _tenorDataRepository.GetAppTestDataModel(files)); + } + else if (Request.Form.ContainsKey("DownloadFile")) + { + return File(JsonSerializer.SerializeToUtf8Bytes(await _tenorDataRepository.GetAppTestDataModel(files)), "application/json", "testData.json"); + } + else if (Request.Form.ContainsKey("Delete")) + { + foreach (var file in files) + { + _tenorDataRepository.DeleteFile(file); + } + + return RedirectToAction("Index"); + } + else + { + throw new Exception("Unknown action"); + } + } +} \ No newline at end of file diff --git a/src/Models/Authentication/CustomClaim.cs b/src/Models/Authentication/CustomClaim.cs index 9283c96c..1d14df2e 100644 --- a/src/Models/Authentication/CustomClaim.cs +++ b/src/Models/Authentication/CustomClaim.cs @@ -1,21 +1,26 @@ -namespace Altinn.Platform.Authentication.Model +using System.Text.Json.Serialization; + +namespace Altinn.Platform.Authentication.Model { public class CustomClaim { /// /// Gets or sets the claim type, E.g. custom:claim /// + [JsonPropertyName("type")] public string Type { get; set; } /// /// Gets or sets the claim value, E.g. customValue /// + [JsonPropertyName("value")] public string Value { get; set; } /// /// Gets or sets the value type for the claim, E.g. http://www.w3.org/2001/XMLSchema#string /// See System.Security.Claims.ClaimValueTypes for more value types /// + [JsonPropertyName("valueType")] public string ValueType { get; set; } } } diff --git a/src/Models/Authorization/Role.cs b/src/Models/Authorization/Role.cs index a67fe7f6..a120f821 100644 --- a/src/Models/Authorization/Role.cs +++ b/src/Models/Authorization/Role.cs @@ -1,7 +1,5 @@ using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; +using System.Text.Json.Serialization; namespace Authorization.Interface.Models { @@ -15,12 +13,14 @@ public class Role /// Gets or sets the role type /// [JsonProperty] + [JsonPropertyName("value")] public string Type { get; set; } /// /// Gets or sets the role /// [JsonProperty] + [JsonPropertyName("value")] public string Value { get; set; } } } diff --git a/src/Models/TenorViewModel.cs b/src/Models/TenorViewModel.cs new file mode 100644 index 00000000..4d4861a9 --- /dev/null +++ b/src/Models/TenorViewModel.cs @@ -0,0 +1,19 @@ +#nullable enable +namespace LocalTest.Models; + +using LocalTest.Services.Tenor.Models; +using LocalTest.Services.TestData; + +public class TenorViewModel +{ + public List FileItems { get; set; } = default!; + public AppTestDataModel? AppUsers { get; set; } +} + +public class TenorFileItem +{ + public string FileName { get; set; } = default!; + public string RawFileContent { get; set; } = default!; + public Freg? Freg { get; set; } + public BrregErFr? Brreg { get; set; } +} \ No newline at end of file diff --git a/src/Services/Tenor/Models/BrregErFr.cs b/src/Services/Tenor/Models/BrregErFr.cs new file mode 100644 index 00000000..1cdda1c3 --- /dev/null +++ b/src/Services/Tenor/Models/BrregErFr.cs @@ -0,0 +1,365 @@ +#nullable disable +namespace LocalTest.Services.Tenor.Models; + + +using System.Text.Json.Serialization; + +public class BrregErFr +{ + /// + /// Internally assigned property that is Altinn's inernal ID + /// + [JsonIgnore] + public int PartyId { get; set; } + + [JsonPropertyName("organisasjonsnummer")] + public string Organisasjonsnummer { get; set; } + + [JsonPropertyName("navn")] + public string Navn { get; set; } + + [JsonPropertyName("oppdeltNavn")] + public List OppdeltNavn { get; set; } + + [JsonPropertyName("organisasjonsform")] + public Organisasjonsform Organisasjonsform { get; set; } + + [JsonPropertyName("forretningsadresse")] + public Forretningsadresse Forretningsadresse { get; set; } + + [JsonPropertyName("postadresse")] + public Postadresse Postadresse { get; set; } + + [JsonPropertyName("naeringskoder")] + public List Naeringskoder { get; set; } + + [JsonPropertyName("institusjonellSektorkode")] + public InstitusjonellSektorkode InstitusjonellSektorkode { get; set; } + + [JsonPropertyName("registreringsdatoEnhetsregisteret")] + public string RegistreringsdatoEnhetsregisteret { get; set; } + + [JsonPropertyName("slettetIEnhetsregisteret")] + public string SlettetIEnhetsregisteret { get; set; } + + [JsonPropertyName("registrertIForetaksregisteret")] + public string RegistrertIForetaksregisteret { get; set; } + + [JsonPropertyName("registreringsdatoForetaksregisteret")] + public string RegistreringsdatoForetaksregisteret { get; set; } + + [JsonPropertyName("slettetIForetaksregisteret")] + public string SlettetIForetaksregisteret { get; set; } + + [JsonPropertyName("registreringspliktigForetaksregisteret")] + public string RegistreringspliktigForetaksregisteret { get; set; } + + [JsonPropertyName("registrertIFrivillighetsregisteret")] + public string RegistrertIFrivillighetsregisteret { get; set; } + + [JsonPropertyName("registrertIStiftelsesregisteret")] + public string RegistrertIStiftelsesregisteret { get; set; } + + [JsonPropertyName("registrertIMvaregisteret")] + public string RegistrertIMvaregisteret { get; set; } + + [JsonPropertyName("stiftelsesdato")] + public string Stiftelsesdato { get; set; } + + [JsonPropertyName("aktivitetBransje")] + public List AktivitetBransje { get; set; } + + [JsonPropertyName("vedtektsdato")] + public string Vedtektsdato { get; set; } + + [JsonPropertyName("vedtektsfestetFormaal")] + public List VedtektsfestetFormaal { get; set; } + + [JsonPropertyName("sisteInnsendteAarsregnskap")] + public string SisteInnsendteAarsregnskap { get; set; } + + [JsonPropertyName("konkurs")] + public string Konkurs { get; set; } + + [JsonPropertyName("underAvvikling")] + public string UnderAvvikling { get; set; } + + [JsonPropertyName("underTvangsavviklingEllerTvangsopplosning")] + public string UnderTvangsavviklingEllerTvangsopplosning { get; set; } + + [JsonPropertyName("maalform")] + public string Maalform { get; set; } + + [JsonPropertyName("ansvarsbegrensning")] + public string Ansvarsbegrensning { get; set; } + + [JsonPropertyName("harAnsatte")] + public string HarAnsatte { get; set; } + + [JsonPropertyName("antallAnsatte")] + public int? AntallAnsatte { get; set; } + + [JsonPropertyName("underenhet")] + public Underenhet Underenhet { get; set; } + + [JsonPropertyName("bedriftsforsamling")] + public string Bedriftsforsamling { get; set; } + + [JsonPropertyName("representantskap")] + public string Representantskap { get; set; } + + [JsonPropertyName("enhetstatuser")] + public List Enhetstatuser { get; set; } + + [JsonPropertyName("fullmakter")] + public List Fullmakter { get; set; } + + [JsonPropertyName("frivilligMvaRegistrert")] + public List FrivilligMvaRegistrert { get; set; } + + [JsonPropertyName("finansielleInstrumenter")] + public List FinansielleInstrumenter { get; set; } + + [JsonPropertyName("kapital")] + public Kapital Kapital { get; set; } + + [JsonPropertyName("kjonnsrepresentasjon")] + public string Kjonnsrepresentasjon { get; set; } + + [JsonPropertyName("matrikkelnummer")] + public List Matrikkelnummer { get; set; } + + [JsonPropertyName("paategninger")] + public List Paategninger { get; set; } + + [JsonPropertyName("fravalgAvRevisjon")] + public FravalgAvRevisjon FravalgAvRevisjon { get; set; } + + [JsonPropertyName("norskregistrertUtenlandskForetak")] + public NorskregistrertUtenlandskForetak NorskregistrertUtenlandskForetak { get; set; } + + [JsonPropertyName("lovgivningOgForetaksformIHjemlandet")] + public LovgivningOgForetaksformIHjemlandet LovgivningOgForetaksformIHjemlandet { get; set; } + + [JsonPropertyName("registerIHjemlandet")] + public RegisterIHjemlandet RegisterIHjemlandet { get; set; } + + [JsonPropertyName("fusjoner")] + public List Fusjoner { get; set; } + + [JsonPropertyName("fisjoner")] + public List Fisjoner { get; set; } + + [JsonPropertyName("rollegrupper")] + public List Rollegrupper { get; set; } +} + +public class Forretningsadresse +{ + [JsonPropertyName("land")] + public string Land { get; set; } + + [JsonPropertyName("landkode")] + public string Landkode { get; set; } + + [JsonPropertyName("postnummer")] + public string Postnummer { get; set; } + + [JsonPropertyName("poststed")] + public string Poststed { get; set; } + + [JsonPropertyName("adresse")] + public List Adresse { get; set; } + + [JsonPropertyName("kommune")] + public string Kommune { get; set; } + + [JsonPropertyName("kommunenummer")] + public string Kommunenummer { get; set; } +} + +public class FravalgAvRevisjon +{ + [JsonPropertyName("fravalg")] + public string Fravalg { get; set; } +} + +public class Fritekst +{ + [JsonPropertyName("plassering")] + public string Plassering { get; set; } +} + +public class InstitusjonellSektorkode +{ + [JsonPropertyName("kode")] + public string Kode { get; set; } + + [JsonPropertyName("beskrivelse")] + public string Beskrivelse { get; set; } +} + +public class Kapital +{ + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("belop")] + public string Belop { get; set; } + + [JsonPropertyName("antallAksjer")] + public string AntallAksjer { get; set; } + + [JsonPropertyName("innbetaltBelop")] + public string InnbetaltBelop { get; set; } + + [JsonPropertyName("fritekst")] + public List Fritekst { get; set; } + + [JsonPropertyName("fulltInnbetaltBelop")] + public string FulltInnbetaltBelop { get; set; } + + [JsonPropertyName("sakkyndigRedegjorelse")] + public string SakkyndigRedegjorelse { get; set; } +} + +public class LovgivningOgForetaksformIHjemlandet +{ + [JsonPropertyName("foretaksform")] + public string Foretaksform { get; set; } +} + +public class Naeringskoder +{ + [JsonPropertyName("kode")] + public string Kode { get; set; } + + [JsonPropertyName("beskrivelse")] + public string Beskrivelse { get; set; } + + [JsonPropertyName("hjelpeenhetskode")] + public bool? Hjelpeenhetskode { get; set; } + + [JsonPropertyName("rekkefolge")] + public int? Rekkefolge { get; set; } + + [JsonPropertyName("nivaa")] + public int? Nivaa { get; set; } +} + +public class NorskregistrertUtenlandskForetak +{ + [JsonPropertyName("helNorskEierskap")] + public string HelNorskEierskap { get; set; } + + [JsonPropertyName("aktivitetINorge")] + public string AktivitetINorge { get; set; } +} + +public class Organisasjonsform +{ + [JsonPropertyName("kode")] + public string Kode { get; set; } + + [JsonPropertyName("beskrivelse")] + public string Beskrivelse { get; set; } +} + +public class Person +{ + [JsonPropertyName("foedselsnummer")] + public string Foedselsnummer { get; set; } +} + +public class Postadresse +{ + [JsonPropertyName("land")] + public string Land { get; set; } + + [JsonPropertyName("landkode")] + public string Landkode { get; set; } + + [JsonPropertyName("postnummer")] + public string Postnummer { get; set; } + + [JsonPropertyName("poststed")] + public string Poststed { get; set; } + + [JsonPropertyName("adresse")] + public List Adresse { get; set; } + + [JsonPropertyName("kommune")] + public string Kommune { get; set; } + + [JsonPropertyName("kommunenummer")] + public string Kommunenummer { get; set; } +} + +public class RegisterIHjemlandet +{ + [JsonPropertyName("navnRegister")] + public List NavnRegister { get; set; } + + [JsonPropertyName("adresse")] + public List Adresse { get; set; } +} + +public class Rollegrupper +{ + [JsonPropertyName("type")] + public Type Type { get; set; } + + [JsonPropertyName("fritekst")] + public List Fritekst { get; set; } + + [JsonPropertyName("roller")] + public List Roller { get; set; } +} + +public class Roller +{ + [JsonPropertyName("type")] + public Type Type { get; set; } + + [JsonPropertyName("person")] + public Person Person { get; set; } + + [JsonPropertyName("virksomhet")] + public Virksomhet Virksomhet { get; set; } + + [JsonPropertyName("valgtAv")] + public ValgtAv ValgtAv { get; set; } + + [JsonPropertyName("fratraadt")] + public string Fratraadt { get; set; } + + [JsonPropertyName("fritekst")] + public List Fritekst { get; set; } + + [JsonPropertyName("rekkefolge")] + public int? Rekkefolge { get; set; } +} + + + +public class Type +{ + [JsonPropertyName("kode")] + public string Kode { get; set; } + + [JsonPropertyName("beskrivelse")] + public string Beskrivelse { get; set; } +} + +public class Underenhet +{ +} + +public class ValgtAv +{ +} + +public class Virksomhet +{ +} + diff --git a/src/Services/Tenor/Models/Freg.cs b/src/Services/Tenor/Models/Freg.cs new file mode 100644 index 00000000..5c6b3842 --- /dev/null +++ b/src/Services/Tenor/Models/Freg.cs @@ -0,0 +1,342 @@ +#nullable disable +namespace LocalTest.Services.Tenor.Models; + +using System.Text.Json.Serialization; + +public class Freg +{ + /// + /// Internally assigned property that is Altinn's inernal ID + /// + [JsonIgnore] + public int PartyId { get; set; } + + [JsonPropertyName("identifikasjonsnummer")] + public List Identifikasjonsnummer { get; set; } + + [JsonPropertyName("status")] + public List Status { get; set; } + + [JsonPropertyName("kjoenn")] + public List Kjoenn { get; set; } + + [JsonPropertyName("foedsel")] + public List Foedsel { get; set; } + + [JsonPropertyName("foedselINorge")] + public List FoedselINorge { get; set; } + + [JsonPropertyName("familierelasjon")] + public List Familierelasjon { get; set; } + + [JsonPropertyName("sivilstand")] + public List Sivilstand { get; set; } + + [JsonPropertyName("navn")] + public List Navn { get; set; } + + [JsonPropertyName("bostedsadresse")] + public List Bostedsadresse { get; set; } + + [JsonPropertyName("statsborgerskap")] + public List Statsborgerskap { get; set; } +} + +public class Adressenummer +{ + [JsonPropertyName("husnummer")] + public string Husnummer { get; set; } +} + +public class Bostedsadresse +{ + [JsonPropertyName("ajourholdstidspunkt")] + public DateTime? Ajourholdstidspunkt { get; set; } + + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("aarsak")] + public string Aarsak { get; set; } + + [JsonPropertyName("gyldighetstidspunkt")] + public DateTime? Gyldighetstidspunkt { get; set; } + + [JsonPropertyName("vegadresse")] + public Vegadresse Vegadresse { get; set; } + + [JsonPropertyName("adresseIdentifikatorFraMatrikkelen")] + public string AdresseIdentifikatorFraMatrikkelen { get; set; } + + [JsonPropertyName("adressegradering")] + public string Adressegradering { get; set; } + + [JsonPropertyName("flyttedato")] + public string Flyttedato { get; set; } + + [JsonPropertyName("grunnkrets")] + public int? Grunnkrets { get; set; } + + [JsonPropertyName("stemmekrets")] + public int? Stemmekrets { get; set; } + + [JsonPropertyName("skolekrets")] + public int? Skolekrets { get; set; } + + [JsonPropertyName("kirkekrets")] + public int? Kirkekrets { get; set; } +} + +public class Familierelasjon +{ + [JsonPropertyName("ajourholdstidspunkt")] + public DateTime? Ajourholdstidspunkt { get; set; } + + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("aarsak")] + public string Aarsak { get; set; } + + [JsonPropertyName("gyldighetstidspunkt")] + public DateTime? Gyldighetstidspunkt { get; set; } + + [JsonPropertyName("relatertPerson")] + public string RelatertPerson { get; set; } + + [JsonPropertyName("relatertPersonsRolle")] + public string RelatertPersonsRolle { get; set; } + + [JsonPropertyName("minRolleForPerson")] + public string MinRolleForPerson { get; set; } +} + +public class Foedsel +{ + [JsonPropertyName("ajourholdstidspunkt")] + public DateTime? Ajourholdstidspunkt { get; set; } + + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("gyldighetstidspunkt")] + public DateTime? Gyldighetstidspunkt { get; set; } + + [JsonPropertyName("foedselsdato")] + public string Foedselsdato { get; set; } + + [JsonPropertyName("foedselsaar")] + public string Foedselsaar { get; set; } + + [JsonPropertyName("foedekommuneINorge")] + public string FoedekommuneINorge { get; set; } + + [JsonPropertyName("foedeland")] + public string Foedeland { get; set; } +} + +public class FoedselINorge +{ + [JsonPropertyName("ajourholdstidspunkt")] + public DateTime? Ajourholdstidspunkt { get; set; } + + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("aarsak")] + public string Aarsak { get; set; } + + [JsonPropertyName("gyldighetstidspunkt")] + public DateTime? Gyldighetstidspunkt { get; set; } + + [JsonPropertyName("foedselsinstitusjonsnavn")] + public string Foedselsinstitusjonsnavn { get; set; } + + [JsonPropertyName("rekkefoelgenummer")] + public int? Rekkefoelgenummer { get; set; } +} + +public class Identifikasjonsnummer +{ + [JsonPropertyName("ajourholdstidspunkt")] + public DateTime? Ajourholdstidspunkt { get; set; } + + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("status")] + public string Status { get; set; } + + [JsonPropertyName("foedselsEllerDNummer")] + public string FoedselsEllerDNummer { get; set; } + + [JsonPropertyName("identifikatortype")] + public string Identifikatortype { get; set; } +} + +public class KjoennType +{ + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("kjoenn")] + public string Kjoenn { get; set; } +} + +public class Navn +{ + [JsonPropertyName("ajourholdstidspunkt")] + public DateTime? Ajourholdstidspunkt { get; set; } + + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("aarsak")] + public string Aarsak { get; set; } + + [JsonPropertyName("gyldighetstidspunkt")] + public DateTime? Gyldighetstidspunkt { get; set; } + + [JsonPropertyName("fornavn")] + public string Fornavn { get; set; } + + [JsonPropertyName("mellomnavn")] + public string Mellomnavn { get; set; } + + [JsonPropertyName("etternavn")] + public string Etternavn { get; set; } +} + +public class Poststed +{ + [JsonPropertyName("poststedsnavn")] + public string Poststedsnavn { get; set; } + + [JsonPropertyName("postnummer")] + public string Postnummer { get; set; } +} + + +public class SivilstandType +{ + [JsonPropertyName("ajourholdstidspunkt")] + public DateTime? Ajourholdstidspunkt { get; set; } + + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("aarsak")] + public string Aarsak { get; set; } + + [JsonPropertyName("gyldighetstidspunkt")] + public DateTime? Gyldighetstidspunkt { get; set; } + + [JsonPropertyName("sivilstand")] + public string Sivilstand { get; set; } + + [JsonPropertyName("sivilstandsdato")] + public string Sivilstandsdato { get; set; } + + [JsonPropertyName("myndighet")] + public string Myndighet { get; set; } + + [JsonPropertyName("kommune")] + public string Kommune { get; set; } + + [JsonPropertyName("sted")] + public string Sted { get; set; } + + [JsonPropertyName("relatertVedSivilstand")] + public string RelatertVedSivilstand { get; set; } +} + +public class StatsborgerskapType +{ + [JsonPropertyName("ajourholdstidspunkt")] + public DateTime? Ajourholdstidspunkt { get; set; } + + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("aarsak")] + public string Aarsak { get; set; } + + [JsonPropertyName("gyldighetstidspunkt")] + public DateTime? Gyldighetstidspunkt { get; set; } + + [JsonPropertyName("statsborgerskap")] + public string Statsborgerskap { get; set; } + + [JsonPropertyName("ervervsdato")] + public string Ervervsdato { get; set; } +} + +public class StatusType +{ + [JsonPropertyName("ajourholdstidspunkt")] + public DateTime? Ajourholdstidspunkt { get; set; } + + [JsonPropertyName("erGjeldende")] + public bool? ErGjeldende { get; set; } + + [JsonPropertyName("kilde")] + public string Kilde { get; set; } + + [JsonPropertyName("gyldighetstidspunkt")] + public DateTime? Gyldighetstidspunkt { get; set; } + + [JsonPropertyName("status")] + public string Status { get; set; } +} + +public class Vegadresse +{ + [JsonPropertyName("kommunenummer")] + public string Kommunenummer { get; set; } + + [JsonPropertyName("bruksenhetsnummer")] + public string Bruksenhetsnummer { get; set; } + + [JsonPropertyName("bruksenhetstype")] + public string Bruksenhetstype { get; set; } + + [JsonPropertyName("adressenavn")] + public string Adressenavn { get; set; } + + [JsonPropertyName("adressenummer")] + public Adressenummer Adressenummer { get; set; } + + [JsonPropertyName("adressekode")] + public string Adressekode { get; set; } + + [JsonPropertyName("poststed")] + public Poststed Poststed { get; set; } +} + diff --git a/src/Services/Tenor/TenorDataReader.cs b/src/Services/Tenor/TenorDataReader.cs new file mode 100644 index 00000000..45e8300f --- /dev/null +++ b/src/Services/Tenor/TenorDataReader.cs @@ -0,0 +1,198 @@ +#nullable enable +using System.Text.Json; +using Authorization.Interface.Models; +using LocalTest.Configuration; +using LocalTest.Models; +using LocalTest.Services.Tenor.Models; +using Microsoft.Extensions.Options; + +namespace LocalTest.Services.TestData; + +public class TenorDataRepository +{ + private static readonly JsonSerializerOptions _options = new(JsonSerializerDefaults.Web) + { + Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() } + }; + private readonly LocalPlatformSettings _settings; + + public TenorDataRepository(IOptions settings) + { + _settings = settings.Value; + } + + public DirectoryInfo GetTenorStorageDirectory() + { + return new DirectoryInfo(Path.Join(_settings.LocalTestingStorageBasePath, _settings.TenorDataFolder)); + } + + + public async Task<(List, List)> ReadFromDisk(List? files = null) + { + var freg = new List(); + var brregErFr = new List(); + var tenorFolder = GetTenorStorageDirectory(); + if (!tenorFolder.Exists) + { + return (brregErFr, freg); + } + + foreach (var fregFile in tenorFolder.GetFiles("freg.*.kildedata.json").Where(f => files?.Contains(f.Name) ?? true)) + { + var fileBytes = await File.ReadAllBytesAsync(fregFile.FullName); + var fileData = JsonSerializer.Deserialize(fileBytes, _options); + if (fileData is not null) + freg.Add(fileData); + } + foreach (var brregFile in tenorFolder.GetFiles("brreg-er-fr.*.kildedata.json").Where(f => files?.Contains(f.Name) ?? true)) + { + var fileBytes = await File.ReadAllBytesAsync(brregFile.FullName); + var fileData = JsonSerializer.Deserialize(fileBytes, _options); + if (fileData is not null) + brregErFr.Add(fileData); + } + + return (brregErFr, freg); + } + + public async Task GetAppTestDataModel(List? files = null) + { + var (brreg, freg) = await ReadFromDisk(files); + // Assign partyId to all entities + int partyId = 600000; + freg.ForEach(f => f.PartyId = partyId++); + partyId = 700000; + brreg.ForEach(b => b.PartyId = partyId++); + + + var roles = brreg.SelectMany(b => b.Rollegrupper.SelectMany(rg => rg.Roller.Select(r => (b.PartyId, r.Type.Kode, r.Person.Foedselsnummer)))).Where(r => r.Foedselsnummer is not null); + var fnrRoleLookup = roles.GroupBy(r => r.Foedselsnummer).ToDictionary(role => role.Key, role => role.GroupBy(r => r.PartyId).ToDictionary(k => k.Key, k => k.Select(tuple => new Role { Type = "Altinn", Value = tuple.Kode }).ToList())); + + + int userId = 10000; + return new() + { + Persons = freg.Select(f => + { + var fnr = f.Identifikasjonsnummer.FirstErGjeldende()?.FoedselsEllerDNummer ?? throw new Exception("fødselsnummer ikke funnet"); + var adresse = f.Bostedsadresse.FirstErGjeldende() ?? throw new Exception("Mangler bostedsadresse"); + return new AppTestPerson + { + UserId = userId++, + PartyId = f.PartyId, + SSN = fnr, + FirstName = f.Navn.FirstErGjeldende()?.Fornavn ?? "Ukjent", + LastName = f.Navn.FirstErGjeldende()?.Etternavn ?? "Ukjent", + MiddleName = f.Navn.FirstErGjeldende()?.Mellomnavn, + UserName = $"user-{99999999999 - long.Parse(fnr)}", // Make an sytnetic username based on an obfuscated fnr + AddressCity = adresse.Vegadresse.Poststed.Poststedsnavn, + // AddressMunicipalName = adresse.Vegadresse.Kommunenummer, + AddressMunicipalNumber = adresse.Vegadresse.Kommunenummer, + AddressHouseLetter = f.Bostedsadresse.FirstErGjeldende()?.Vegadresse.Adressekode, + AddressHouseNumber = f.Bostedsadresse.FirstErGjeldende()?.Vegadresse.Adressenummer.Husnummer, + AddressMunicipalName = null, + AddressPostalCode = null, + AddressStreetName = null, + Email = null, + MobileNumber = null, + TelephoneNumber = null, + Language = null, + MailingAddress = null, + MailingPostalCity = null, + MailingPostalCode = null, + PartyRoles = fnrRoleLookup.TryGetValue(fnr, out var partyRoles) ? partyRoles : new Dictionary>(), + CustomClaims = new() + { + new() + { + Type = "user:source", + ValueType = "http://www.w3.org/2001/XMLSchema#string", + Value = "localTenor" + } + }, + + }; + }).ToList(), + Orgs = brreg.Select(b => new AppTestOrg + { + PartyId = b.PartyId, + ParentPartyId = null, + OrgNumber = b.Organisasjonsnummer, + Name = b.Navn, + BusinessAddress = string.Join("\n", b.Forretningsadresse.Adresse), + BusinessPostalCity = b.Forretningsadresse.Poststed, + BusinessPostalCode = b.Forretningsadresse.Postnummer, + MailingAddress = string.Join("\n", b.Postadresse.Adresse), + MailingPostalCity = b.Postadresse.Poststed, + MailingPostalCode = b.Postadresse.Postnummer, + EMailAddress = null, + FaxNumber = null, + InternetAddress = null, + MobileNumber = null, + TelephoneNumber = null, + UnitStatus = null, + UnitType = null, + }).ToList(), + }; + } + + public async Task StoreUploadedFile(IFormFile file) + { + var dir = GetTenorStorageDirectory(); + var filename = new FileInfo(Path.Join(dir.FullName, file.FileName)); + if (filename.Directory?.FullName != dir.FullName) + { + throw new Exception($"Invalid filename {file.FileName}"); + } + using Stream fileStream = filename.OpenWrite(); + await file.CopyToAsync(fileStream); + } + private static T? ParseCatchException(string rawJson) where T : class + { + try + { + return JsonSerializer.Deserialize(rawJson, _options); + } + catch (Exception) + { + return null; + } + } + public async Task> GetFileItems() + { + var directory = GetTenorStorageDirectory(); + var itemList = new List(); + foreach (var f in directory.GetFiles()) + { + if (f.Name.StartsWith('.')) + { + continue; // Ignore hidden files (like .DS_Store) + } + var content = await System.IO.File.ReadAllTextAsync(f.FullName); + + itemList.Add(new() + { + FileName = f.Name, + RawFileContent = content, + Freg = ParseCatchException(content), + Brreg = ParseCatchException(content), + }); + }; + return itemList; + } + + public void DeleteFile(string fileName) + { + var fileHandle = GetTenorStorageDirectory().GetFiles(fileName).First(f => f.Name == fileName); + fileHandle.Delete(); + } +} + +public static class ListExtentions +{ + public static T? FirstErGjeldende(this List list) + { + var erGjeldendeAccessor = typeof(T).GetProperty("erGjeldende"); + return list.FirstOrDefault(t => erGjeldendeAccessor?.GetValue(t) as bool? == true) ?? list.FirstOrDefault(); + } +} \ No newline at end of file diff --git a/src/Services/TestData/AppTestDataModel.cs b/src/Services/TestData/AppTestDataModel.cs index b97792e8..a6ead9e1 100644 --- a/src/Services/TestData/AppTestDataModel.cs +++ b/src/Services/TestData/AppTestDataModel.cs @@ -1,4 +1,5 @@ #nullable enable +using System.Text.Json.Serialization; using Altinn.Platform.Authentication.Model; using Altinn.Platform.Profile.Models; using Altinn.Platform.Register.Models; @@ -12,7 +13,12 @@ namespace LocalTest.Services.TestData; /// public class AppTestDataModel { + [JsonPropertyOrder(int.MinValue)] + [JsonPropertyName("$schema")] + public string Schema => "https://altinncdn.no/schemas/json/test-users/test-users.schema.v1.json"; + [JsonPropertyName("persons")] public List Persons { get; set; } = default!; + [JsonPropertyName("orgs")] public List Orgs { get; set; } = default!; public TestDataModel GetTestDataModel() @@ -155,27 +161,49 @@ public static AppTestDataModel FromTestDataModel(TestDataModel localData) }; return constructedAppData; } + + public bool IsEmpty() + { + return Persons.Count == 0 && Orgs.Count == 0; + } } public class AppTestOrg { + [JsonPropertyName("partyId")] public int PartyId { get; set; } + [JsonPropertyName("orgNumber")] public string OrgNumber { get; set; } = default!; + [JsonPropertyName("parentPartyId")] public int? ParentPartyId { get; set; } + [JsonPropertyName("name")] public string? Name { get; set; } + [JsonPropertyName("businessAddress")] public string? BusinessAddress { get; set; } + [JsonPropertyName("businessPostalCity")] public string? BusinessPostalCity { get; set; } + [JsonPropertyName("businessPostalCode")] public string? BusinessPostalCode { get; set; } + [JsonPropertyName("eMailAddress")] public string? EMailAddress { get; set; } + [JsonPropertyName("faxNumber")] public string? FaxNumber { get; set; } + [JsonPropertyName("internetAddress")] public string? InternetAddress { get; set; } + [JsonPropertyName("mailingAddress")] public string? MailingAddress { get; set; } + [JsonPropertyName("mailingPostalCity")] public string? MailingPostalCity { get; set; } + [JsonPropertyName("mailingPostalCode")] public string? MailingPostalCode { get; set; } + [JsonPropertyName("mobileNumber")] public string? MobileNumber { get; set; } + [JsonPropertyName("telephoneNumber")] public string? TelephoneNumber { get; set; } + [JsonPropertyName("unitStatus")] public string? UnitStatus { get; set; } + [JsonPropertyName("unitType")] public string? UnitType { get; set; } public Party ToParty(List? potentialChildOrgs = null) @@ -226,28 +254,51 @@ public Party ToParty(List? potentialChildOrgs = null) public class AppTestPerson { + [JsonPropertyName("partyId")] public int PartyId { get; set; } = default!; + [JsonPropertyName("ssn")] public string SSN { get; set; } = default!; - public string FirstName { get; set; } = default!; - public string MiddleName { get; set; } = default!; + [JsonPropertyName("firstName")] + public string? FirstName { get; set; } = default!; + [JsonPropertyName("middleName")] + public string? MiddleName { get; set; } + [JsonPropertyName("lastName")] public string LastName { get; set; } = default!; + [JsonPropertyName("customClaims")] public List CustomClaims { get; set; } = new(); + [JsonPropertyName("partyRoles")] public Dictionary> PartyRoles { get; set; } = new(); + [JsonPropertyName("addressCity")] public string? AddressCity { get; set; } + [JsonPropertyName("addressHouseLetter")] public string? AddressHouseLetter { get; set; } + [JsonPropertyName("addressHouseNumber")] public string? AddressHouseNumber { get; set; } + [JsonPropertyName("addressMunicipalName")] public string? AddressMunicipalName { get; set; } + [JsonPropertyName("addressMunicipalNumber")] public string? AddressMunicipalNumber { get; set; } + [JsonPropertyName("addressPostalCode")] public string? AddressPostalCode { get; set; } + [JsonPropertyName("addressStreetName")] public string? AddressStreetName { get; set; } + [JsonPropertyName("mailingAddress")] public string? MailingAddress { get; set; } + [JsonPropertyName("mailingPostalCity")] public string? MailingPostalCity { get; set; } + [JsonPropertyName("mailingPostalCode")] public string? MailingPostalCode { get; set; } + [JsonPropertyName("mobileNumber")] public string? MobileNumber { get; set; } + [JsonPropertyName("telephoneNumber")] public string? TelephoneNumber { get; set; } + [JsonPropertyName("email")] public string? Email { get; set; } + [JsonPropertyName("userId")] public int UserId { get; set; } + [JsonPropertyName("language")] public string? Language { get; set; } + [JsonPropertyName("userName")] public string? UserName { get; set; } public string GetFullName() => string.IsNullOrWhiteSpace(MiddleName) ? $"{FirstName} {LastName}" : $"{FirstName} {MiddleName} {LastName}"; diff --git a/src/Services/TestData/TestDataService.cs b/src/Services/TestData/TestDataService.cs index aca43d46..816f0a7a 100644 --- a/src/Services/TestData/TestDataService.cs +++ b/src/Services/TestData/TestDataService.cs @@ -9,13 +9,15 @@ namespace LocalTest.Services.TestData; public class TestDataService { private readonly ILocalApp _localApp; - private readonly LocalPlatformSettings _settings; + private readonly TenorDataRepository _tenorDataRepository; private readonly IMemoryCache _cache; + private readonly LocalPlatformSettings _settings; private readonly ILogger _logger; - public TestDataService(ILocalApp localApp, IOptions settings, IMemoryCache memoryCache, ILogger logger) + public TestDataService(ILocalApp localApp, TenorDataRepository tenorDataRepository, IOptions settings, IMemoryCache memoryCache, ILogger logger) { - _cache = memoryCache; _localApp = localApp; + _tenorDataRepository = tenorDataRepository; + _cache = memoryCache; _settings = settings.Value; _logger = logger; } @@ -42,6 +44,14 @@ public async Task GetTestData() _logger.LogInformation(e, "Fetching Test data from app failed."); } + var tenorUsers = await _tenorDataRepository.GetAppTestDataModel(); + if (tenorUsers is not null && !tenorUsers.IsEmpty()) + { + // Use tenor users if they exist + return tenorUsers.GetTestDataModel(); + } + + //Fallback to Ola Nordmann, Sofie Salt ... if no other users are availible return await TestDataDiskReader.ReadFromDisk(_settings.LocalTestingStaticTestDataPath); }))!; } diff --git a/src/Startup.cs b/src/Startup.cs index dbd1ba94..532a7f28 100644 --- a/src/Startup.cs +++ b/src/Startup.cs @@ -109,6 +109,7 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Views/Home/FrontendVersion.cshtml b/src/Views/FrontendVersion/Index.cshtml similarity index 66% rename from src/Views/Home/FrontendVersion.cshtml rename to src/Views/FrontendVersion/Index.cshtml index 80787a16..d2ed08ed 100644 --- a/src/Views/Home/FrontendVersion.cshtml +++ b/src/Views/FrontendVersion/Index.cshtml @@ -1,13 +1,13 @@ @model FrontendVersion @{ - ViewData["Title"] = "Frontend version"; + ViewData["Title"] = "Frontend version"; }

@ViewData["Title"]

-@using (Html.BeginForm("FrontendVersion", "Home", FormMethod.Post, new { Class = "form-signin" })) +@using (Html.BeginForm("Index", "FrontendVersion", FormMethod.Post, new { Class = "form-signin" })) { - @Html.AntiForgeryToken(); + @Html.AntiForgeryToken()
@Html.DropDownListFor(model => model.Version, Model.Versions, new { Class = "form-control" }) diff --git a/src/Views/Home/Index.cshtml b/src/Views/Home/Index.cshtml index 43cc4659..c910fb34 100644 --- a/src/Views/Home/Index.cshtml +++ b/src/Views/Home/Index.cshtml @@ -84,7 +84,6 @@ @Html.DropDownListFor(model => model.AuthenticationLevel, Model.AuthenticationLevels, new { Class = "form-control" })
- @if(Model.AppModeIsHttp) {
@@ -95,23 +94,16 @@
} - - } - - - - @if(string.IsNullOrWhiteSpace(Model.LocalFrontendUrl)) - { - diff --git a/src/Views/Shared/_Layout.cshtml b/src/Views/Shared/_Layout.cshtml index f8ea777e..d677f2d0 100644 --- a/src/Views/Shared/_Layout.cshtml +++ b/src/Views/Shared/_Layout.cshtml @@ -4,20 +4,35 @@ @ViewData["Title"] - Altinn Studio - +
@@ -30,9 +45,10 @@
- © 2019 - LocalTest - Privacy + © 2019 - LocalTest
@RenderSection("Scripts", required: false) + diff --git a/src/Views/StorageExplorer/Index.cshtml b/src/Views/StorageExplorer/Index.cshtml new file mode 100644 index 00000000..e345fa0c --- /dev/null +++ b/src/Views/StorageExplorer/Index.cshtml @@ -0,0 +1,9 @@ + + + diff --git a/src/Views/TenorUsers/Index.cshtml b/src/Views/TenorUsers/Index.cshtml new file mode 100644 index 00000000..bf8b8d88 --- /dev/null +++ b/src/Views/TenorUsers/Index.cshtml @@ -0,0 +1,98 @@ +@model LocalTest.Models.TenorViewModel + +@using Microsoft.Extensions.Options +@using LocalTest.Configuration +@using LocalTest.Services.TestData + +@inject IOptions LocalPlatformSettings + + +@{ + ViewData["Title"] = "Local users and orgs"; +} +

@ViewData["Title"]

+ +

+ By default Altinn Studio publishes a limited set of test accouns for local testing. For most apps these will be + enough, but if you have more specific needs, or want the same users as in tt02, you probably want to import data + from Tenor testdata to use for your local development +

+ +

+ Tenor requires login with an official norwegian ID (like BankID), so you currently have to login and download + "kildedata" for the persons and organisations that you want to reference. Make sure you find suitable users and + organisations and download "kildedata" for each org/user. +

+ +
+
+

Last opp tenor kildefiler

+
+ @using (Html.BeginForm("Upload", "TenorUsers", FormMethod.Post, new + { + Class = "form-signin", + enctype = + "multipart/form-data" + })) + { + @Html.AntiForgeryToken() + ; +
+ +
+
+ +
+ } +
+ +@using (Html.BeginForm("Update", "TenorUsers", FormMethod.Post, new { })) +{ + + + + + + + + + + + + @foreach (var item in Model.FileItems) + { + + + + @if (item.Brreg is not null) + { + + + + } + else if (item.Freg is not null) + { + + + + + + } + else + { + + + } + + } + +
VelgFilenamePerson nameFnr
@item.FileName@item.Brreg.Organisasjonsnummer@item.Freg.Identifikasjonsnummer?.FirstErGjeldende()?.FoedselsEllerDNummer@item.Freg.Navn?.FirstErGjeldende()?.Fornavn @item.Freg.Navn?.FirstErGjeldende()?.Mellomnavn + @item.Freg.Navn?.FirstErGjeldende()?.Etternavn +
@item.RawFileContent
+
+ +
+ + +
+} From e85d97f6ece378e85874fc323ab7c616f413daf0 Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Thu, 12 Oct 2023 09:22:44 +0200 Subject: [PATCH 2/7] Fix wrong JsonPropertyName --- src/Models/Authorization/Role.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Models/Authorization/Role.cs b/src/Models/Authorization/Role.cs index a120f821..17acdf07 100644 --- a/src/Models/Authorization/Role.cs +++ b/src/Models/Authorization/Role.cs @@ -13,7 +13,7 @@ public class Role /// Gets or sets the role type /// [JsonProperty] - [JsonPropertyName("value")] + [JsonPropertyName("type")] public string Type { get; set; } /// From 26eef87a7f0d4b6e0f7ca99b927207e1b73c6dea Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Thu, 12 Oct 2023 10:59:51 +0200 Subject: [PATCH 3/7] Small UI improvments --- src/Models/TenorViewModel.cs | 1 + src/Views/TenorUsers/Index.cshtml | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Models/TenorViewModel.cs b/src/Models/TenorViewModel.cs index 4d4861a9..d6ae95f6 100644 --- a/src/Models/TenorViewModel.cs +++ b/src/Models/TenorViewModel.cs @@ -14,6 +14,7 @@ public class TenorFileItem { public string FileName { get; set; } = default!; public string RawFileContent { get; set; } = default!; + public bool Valid => Freg is not null || Brreg is not null; public Freg? Freg { get; set; } public BrregErFr? Brreg { get; set; } } \ No newline at end of file diff --git a/src/Views/TenorUsers/Index.cshtml b/src/Views/TenorUsers/Index.cshtml index bf8b8d88..6bd62449 100644 --- a/src/Views/TenorUsers/Index.cshtml +++ b/src/Views/TenorUsers/Index.cshtml @@ -38,10 +38,10 @@ @Html.AntiForgeryToken() ;
- +
- +
} @@ -61,8 +61,10 @@ @foreach (var item in Model.FileItems) { - - + + + + @item.FileName @if (item.Brreg is not null) { @@ -92,7 +94,7 @@
- - + +
} From a5bb2289e2fc47f420ab50811b02e07e806fbfec Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Fri, 13 Oct 2023 13:19:15 +0200 Subject: [PATCH 4/7] Fix code review issues The main fix is that `GetTenorStorageDirectory` will create the directory if it does not exist. --- src/Controllers/DebugUsersController.cs | 75 +++++++++----------- src/Controllers/FrontendVersionController.cs | 16 ----- src/Controllers/HomeController.cs | 1 - src/Controllers/StorageExplorerController.cs | 25 ------- src/Controllers/TenorUsersController.cs | 15 +--- src/Models/TenorViewModel.cs | 1 - src/Services/Tenor/TenorDataReader.cs | 11 +-- 7 files changed, 40 insertions(+), 104 deletions(-) diff --git a/src/Controllers/DebugUsersController.cs b/src/Controllers/DebugUsersController.cs index 6dfa0289..d31012bc 100644 --- a/src/Controllers/DebugUsersController.cs +++ b/src/Controllers/DebugUsersController.cs @@ -1,14 +1,10 @@ #nullable enable -using System.Text.Json; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; -using Altinn.Platform.Storage.Repository; using LocalTest.Configuration; -using LocalTest.Models; -using LocalTest.Services.LocalApp.Interface; using LocalTest.Services.TestData; using Microsoft.AspNetCore.Authorization; @@ -18,57 +14,52 @@ namespace LocalTest.Controllers; [Route("Home/[controller]/[action]")] public class DebugUsersController : Controller { - private readonly TenorDataRepository _tenorDataRepository; private readonly LocalPlatformSettings _localPlatformSettings; - private readonly ILocalApp _localApp; + private readonly TenorDataRepository _tenorDataRepository; public DebugUsersController( - TenorDataRepository tenorDataRepository, IOptions localPlatformSettings, - ILocalApp localApp) + TenorDataRepository tenorDataRepository) { - _tenorDataRepository = tenorDataRepository; _localPlatformSettings = localPlatformSettings.Value; - _localApp = localApp; + _tenorDataRepository = tenorDataRepository; } - // Debugging endpoint - [AllowAnonymous] - public async Task LocalTestUsersRaw() - { - var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); - - return Json(localData); - } - - //Debugging endpoint - [AllowAnonymous] - public async Task LocalTestUsers() - { - var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); - var constructedAppData = AppTestDataModel.FromTestDataModel(localData); + // Debugging endpoint + [AllowAnonymous] + public async Task LocalTestUsersRaw() + { + var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); - return Json(constructedAppData); - } + return Json(localData); + } - // Debugging endpoint - [AllowAnonymous] - public async Task LocalTestUsersRoundTrip() - { - var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); - var constructedAppData = AppTestDataModel.FromTestDataModel(localData); + //Debugging endpoint + [AllowAnonymous] + public async Task LocalTestUsers() + { + var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); + var constructedAppData = AppTestDataModel.FromTestDataModel(localData); - return Json(constructedAppData.GetTestDataModel()); - } + return Json(constructedAppData); + } - // Debugging endpoint - [AllowAnonymous] - public async Task ShowTenorUsers([FromServices] TenorDataRepository tenorDataRepository) - { - var localData = await tenorDataRepository.GetAppTestDataModel(); + // Debugging endpoint + [AllowAnonymous] + public async Task LocalTestUsersRoundTrip() + { + var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); + var constructedAppData = AppTestDataModel.FromTestDataModel(localData); - return Json(localData); - } + return Json(constructedAppData.GetTestDataModel()); + } + // Debugging endpoint + [AllowAnonymous] + public async Task ShowTenorUsers() + { + var localData = await _tenorDataRepository.GetAppTestDataModel(); + return Json(localData); + } } \ No newline at end of file diff --git a/src/Controllers/FrontendVersionController.cs b/src/Controllers/FrontendVersionController.cs index f22e496f..74db638f 100644 --- a/src/Controllers/FrontendVersionController.cs +++ b/src/Controllers/FrontendVersionController.cs @@ -4,14 +4,11 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; -using Altinn.Platform.Storage.Repository; - using LocalTest.Configuration; using LocalTest.Models; using LocalTest.Services.LocalApp.Interface; using LocalTest.Services.TestData; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Mvc.Rendering; @@ -20,19 +17,6 @@ namespace LocalTest.Controllers; [Route("Home/[controller]/[action]")] public class FrontendVersionController : Controller { - private readonly TenorDataRepository _tenorDataRepository; - private readonly LocalPlatformSettings _localPlatformSettings; - private readonly ILocalApp _localApp; - - public FrontendVersionController( - TenorDataRepository tenorDataRepository, - IOptions localPlatformSettings, - ILocalApp localApp) - { - _tenorDataRepository = tenorDataRepository; - _localPlatformSettings = localPlatformSettings.Value; - _localApp = localApp; - } /// /// See src\development\loadbalancer\nginx.conf /// diff --git a/src/Controllers/HomeController.cs b/src/Controllers/HomeController.cs index 2a2f202d..945b881c 100644 --- a/src/Controllers/HomeController.cs +++ b/src/Controllers/HomeController.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Text.Json; using System.Xml; using Microsoft.AspNetCore.Authentication; diff --git a/src/Controllers/StorageExplorerController.cs b/src/Controllers/StorageExplorerController.cs index 3970ec0c..7085cf93 100644 --- a/src/Controllers/StorageExplorerController.cs +++ b/src/Controllers/StorageExplorerController.cs @@ -1,37 +1,12 @@ #nullable enable -using System.Text.Json; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; - -using Altinn.Platform.Storage.Repository; - -using LocalTest.Configuration; -using LocalTest.Models; -using LocalTest.Services.LocalApp.Interface; - -using LocalTest.Services.TestData; -using Microsoft.AspNetCore.Authorization; namespace LocalTest.Controllers; [Route("Home/[controller]/[action]")] public class StorageExplorerController : Controller { - private readonly TenorDataRepository _tenorDataRepository; - private readonly LocalPlatformSettings _localPlatformSettings; - private readonly ILocalApp _localApp; - - public StorageExplorerController( - TenorDataRepository tenorDataRepository, - IOptions localPlatformSettings, - ILocalApp localApp) - { - _tenorDataRepository = tenorDataRepository; - _localPlatformSettings = localPlatformSettings.Value; - _localApp = localApp; - } - public IActionResult Index() { return View(); diff --git a/src/Controllers/TenorUsersController.cs b/src/Controllers/TenorUsersController.cs index 76492eef..d5c39abe 100644 --- a/src/Controllers/TenorUsersController.cs +++ b/src/Controllers/TenorUsersController.cs @@ -2,11 +2,6 @@ using System.Text.Json; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; - -using Altinn.Platform.Storage.Repository; - -using LocalTest.Configuration; using LocalTest.Models; using LocalTest.Services.LocalApp.Interface; @@ -18,27 +13,19 @@ namespace LocalTest.Controllers; public class TenorUsersController : Controller { private readonly TenorDataRepository _tenorDataRepository; - private readonly ILocalApp _localApp; - public TenorUsersController( - TenorDataRepository tenorDataRepository, - ILocalApp localApp) + public TenorUsersController(TenorDataRepository tenorDataRepository) { _tenorDataRepository = tenorDataRepository; - _localApp = localApp; } public async Task Index() { - var appUsers = await _localApp.GetTestData(); - return View(new TenorViewModel() { - AppUsers = appUsers, FileItems = await _tenorDataRepository.GetFileItems(), - }); } diff --git a/src/Models/TenorViewModel.cs b/src/Models/TenorViewModel.cs index d6ae95f6..d1cb4d9a 100644 --- a/src/Models/TenorViewModel.cs +++ b/src/Models/TenorViewModel.cs @@ -7,7 +7,6 @@ namespace LocalTest.Models; public class TenorViewModel { public List FileItems { get; set; } = default!; - public AppTestDataModel? AppUsers { get; set; } } public class TenorFileItem diff --git a/src/Services/Tenor/TenorDataReader.cs b/src/Services/Tenor/TenorDataReader.cs index 45e8300f..d41cfa6c 100644 --- a/src/Services/Tenor/TenorDataReader.cs +++ b/src/Services/Tenor/TenorDataReader.cs @@ -23,7 +23,12 @@ public TenorDataRepository(IOptions settings) public DirectoryInfo GetTenorStorageDirectory() { - return new DirectoryInfo(Path.Join(_settings.LocalTestingStorageBasePath, _settings.TenorDataFolder)); + var dir = new DirectoryInfo(Path.Join(_settings.LocalTestingStorageBasePath, _settings.TenorDataFolder)); + if (!dir.Exists) + { + dir.Create(); + } + return dir; } @@ -32,10 +37,6 @@ public DirectoryInfo GetTenorStorageDirectory() var freg = new List(); var brregErFr = new List(); var tenorFolder = GetTenorStorageDirectory(); - if (!tenorFolder.Exists) - { - return (brregErFr, freg); - } foreach (var fregFile in tenorFolder.GetFiles("freg.*.kildedata.json").Where(f => files?.Contains(f.Name) ?? true)) { From 5017c884cb6c4e7884b99dcc9624a8bb54e0acb5 Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Thu, 19 Oct 2023 12:35:10 +0200 Subject: [PATCH 5/7] Improve Iframe behaviour of LocalPlatformStorage --- src/Views/Shared/_Layout.cshtml | 6 ---- src/Views/StorageExplorer/Index.cshtml | 42 ++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/Views/Shared/_Layout.cshtml b/src/Views/Shared/_Layout.cshtml index d677f2d0..21c688f6 100644 --- a/src/Views/Shared/_Layout.cshtml +++ b/src/Views/Shared/_Layout.cshtml @@ -42,12 +42,6 @@ @RenderBody() - -
-
- © 2019 - LocalTest -
-
@RenderSection("Scripts", required: false) diff --git a/src/Views/StorageExplorer/Index.cshtml b/src/Views/StorageExplorer/Index.cshtml index e345fa0c..4e4e9c03 100644 --- a/src/Views/StorageExplorer/Index.cshtml +++ b/src/Views/StorageExplorer/Index.cshtml @@ -1,9 +1,39 @@ + + + + + function handleOnLoad(event) { + console.log(iframe.contentDocument.contentType); + if (iframe.contentDocument.contentType != "text/html") { + // Bust out of iframe when the content type isn't text/html + window.location.replace(iframe.contentWindow.location.href); + return; + } + resizeIframe(); + } + iframe.addEventListener("load", handleOnLoad); + window.addEventListener("resize", resizeIframe); + + var downloadButton = document.getElementById("download"); + downloadButton.addEventListener("click", () => { + window.open(iframe.contentWindow.location.href, "_blank"); + }) - + From 33bfdde1b4434ebca29a6bac8573d334a9f3ece0 Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Thu, 19 Oct 2023 12:39:12 +0200 Subject: [PATCH 6/7] Don't let dotnet watch run add script to /LocalPlatformStorage --- loadbalancer/templates/nginx.conf.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/loadbalancer/templates/nginx.conf.conf b/loadbalancer/templates/nginx.conf.conf index 0eb25a28..7e26c95d 100644 --- a/loadbalancer/templates/nginx.conf.conf +++ b/loadbalancer/templates/nginx.conf.conf @@ -113,7 +113,8 @@ http { proxy_pass http://localtest/localtestresources/; } location /LocalPlatformStorage/ { - proxy_pass http://localtest/LocalPlatformStorage/; + proxy_pass http://localtest/LocalPlatformStorage/; + sub_filter '' ''; } location /502LocalTest.html { root /www; From 7f8d372b89561f4229b84c4b9f170b39c7f53fd1 Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Tue, 31 Oct 2023 07:23:44 +0100 Subject: [PATCH 7/7] Mark user adminsitration as preview fix table layout --- src/Views/Shared/_Layout.cshtml | 2 +- src/Views/TenorUsers/Index.cshtml | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Views/Shared/_Layout.cshtml b/src/Views/Shared/_Layout.cshtml index 21c688f6..5bcddce3 100644 --- a/src/Views/Shared/_Layout.cshtml +++ b/src/Views/Shared/_Layout.cshtml @@ -26,7 +26,7 @@ asp-action="Index">Pick frontend version