From 43ec5ab07339a745a62f85e3f2ddfb50f783ed4b Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Mon, 27 Jun 2022 15:56:58 +0200 Subject: [PATCH] Support switching to local js/css from an option in LocalTest (#8562) Co-authored-by: Ole Martin Handeland --- docker-compose.yml | 1 - loadbalancer/Dockerfile | 2 +- loadbalancer/nginx.conf | 23 +++++++- src/Controllers/HomeController.cs | 83 ++++++++++++++++++++++++--- src/Models/FrontendVersion.cs | 12 ++++ src/Models/StartAppModel.cs | 6 ++ src/Views/Home/FrontendVersion.cshtml | 16 ++++++ src/Views/Home/Index.cshtml | 13 +++++ src/Views/Home/Privacy.cshtml | 6 -- 9 files changed, 144 insertions(+), 18 deletions(-) create mode 100644 src/Models/FrontendVersion.cs create mode 100644 src/Views/Home/FrontendVersion.cshtml delete mode 100644 src/Views/Home/Privacy.cshtml diff --git a/docker-compose.yml b/docker-compose.yml index 7efb8fd7..abc6d38a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,6 @@ services: environment: - NGINX_HOST=localhost - NGINX_PORT=80 - - SUB_FILTER=${SUB_FILTER:-} - TEST_DOMAIN=${TEST_DOMAIN:-altinn3local.no} - ALTINN3LOCAL_PORT=${ALTINN3LOCAL_PORT:-80} - NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx diff --git a/loadbalancer/Dockerfile b/loadbalancer/Dockerfile index faf48017..d13d8106 100644 --- a/loadbalancer/Dockerfile +++ b/loadbalancer/Dockerfile @@ -1,4 +1,4 @@ -FROM nginx:1.21.6-alpine +FROM nginx:1.21.6-alpine-perl # Copy to template in order for Environment variable substitutions to run COPY nginx.conf /etc/nginx/templates/nginx.conf.template diff --git a/loadbalancer/nginx.conf b/loadbalancer/nginx.conf index 40fa3e39..f80f99a6 100644 --- a/loadbalancer/nginx.conf +++ b/loadbalancer/nginx.conf @@ -1,9 +1,26 @@ +load_module /etc/nginx/modules/ngx_http_perl_module.so; + worker_processes 1; events { worker_connections 1024; } http { + + # perl_modules perl/lib; + # Support replacing reference to altinn js and css in app with local hosted webpack dev server + perl_set $LOCAL_SUB_FILTER 'sub { + my $r = shift; + my $cookie = $r->header_in("cookie"); + my $url = $1 if ($cookie =~ /.*frontendVersion=(http.*?);/); + if($url) + { + $uri = $r->unescape($url); + return $uri; + } + return "https://altinncdn.no/toolkits/altinn-app-frontend/3/" + }'; + client_max_body_size 50M; sendfile on; @@ -43,9 +60,9 @@ http { } location / { - # Support sub_filter declaration trough environment variable substitution - # default empty - ${SUB_FILTER} + #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; } diff --git a/src/Controllers/HomeController.cs b/src/Controllers/HomeController.cs index 37696448..ebc29a1a 100644 --- a/src/Controllers/HomeController.cs +++ b/src/Controllers/HomeController.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; using System.Security.Claims; using System.Xml; @@ -78,6 +79,7 @@ public async Task Index() model.LocalAppUrl = _localPlatformSettings.LocalAppUrl; var defaultAuthLevel = _localPlatformSettings.LocalAppMode == "http" ? await GetAppAuthLevel(model.TestApps) : 2; model.AuthenticationLevels = GetAuthenticationLevels(defaultAuthLevel); + model.LocalFrontendUrl = HttpContext.Request.Cookies[FRONTEND_URL_COOKIE_NAME]; if (!model.TestApps?.Any() ?? true) { @@ -92,11 +94,6 @@ public async Task Index() return View(model); } - public IActionResult Privacy() - { - return View(); - } - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { @@ -197,6 +194,77 @@ public async Task GetTestOrgToken(string id, [FromQuery] string or return await Task.FromResult(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> GetTestUsers() { List users = new List(); @@ -241,7 +309,8 @@ private async Task> GetTestUsersForList() } private async Task GetAppAuthLevel(IEnumerable testApps) { - try { + try + { var appId = testApps.Single().Value; var policyString = await _localApp.GetXACMLPolicy(appId); var document = new XmlDocument(); @@ -251,7 +320,7 @@ private async Task GetAppAuthLevel(IEnumerable testApps) var authLevelNode = document.SelectSingleNode("/xacml:Policy/xacml:ObligationExpressions/xacml:ObligationExpression[@ObligationId='urn:altinn:obligation:authenticationLevel1']/xacml:AttributeAssignmentExpression[@Category='urn:altinn:minimum-authenticationlevel']/xacml:AttributeValue", nsMngr); return int.Parse(authLevelNode.InnerText); } - catch(Exception) + catch (Exception) { // Return default auth level if Single app auth level can't be found. return 2; diff --git a/src/Models/FrontendVersion.cs b/src/Models/FrontendVersion.cs new file mode 100644 index 00000000..6ab70228 --- /dev/null +++ b/src/Models/FrontendVersion.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc.Rendering; + +using System.Collections.Generic; + +namespace LocalTest.Models +{ + public class FrontendVersion + { + public string Version { get; set; } + public List Versions { get; set; } + } +} diff --git a/src/Models/StartAppModel.cs b/src/Models/StartAppModel.cs index e7b67fd8..50953464 100644 --- a/src/Models/StartAppModel.cs +++ b/src/Models/StartAppModel.cs @@ -69,6 +69,12 @@ public class StartAppModel /// public string AuthenticationLevel { get; set; } + /// + /// Url for where to load the local frontend from + /// (implemented as a cookie that nginx reads and substitutes the content in index.html) + /// + public string LocalFrontendUrl { get; set; } + /// /// List of TestUsers for dropdown /// diff --git a/src/Views/Home/FrontendVersion.cshtml b/src/Views/Home/FrontendVersion.cshtml new file mode 100644 index 00000000..80787a16 --- /dev/null +++ b/src/Views/Home/FrontendVersion.cshtml @@ -0,0 +1,16 @@ +@model FrontendVersion + +@{ + ViewData["Title"] = "Frontend version"; +} +

@ViewData["Title"]

+ +@using (Html.BeginForm("FrontendVersion", "Home", FormMethod.Post, new { Class = "form-signin" })) +{ + @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 0684afd7..6c0889ab 100644 --- a/src/Views/Home/Index.cshtml +++ b/src/Views/Home/Index.cshtml @@ -86,6 +86,19 @@ + + @if(string.IsNullOrWhiteSpace(Model.LocalFrontendUrl)) + { + + } + else + { + + } } } diff --git a/src/Views/Home/Privacy.cshtml b/src/Views/Home/Privacy.cshtml deleted file mode 100644 index af4fb195..00000000 --- a/src/Views/Home/Privacy.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -@{ - ViewData["Title"] = "Privacy Policy"; -} -

@ViewData["Title"]

- -

Use this page to detail your site's privacy policy.