v14: login app (#14318)

* ignore output files

* add new umb-login element

* allow to build and clean 'login' assets

* remove unused AuthUrl since this is now coded into the frontend code for each context

* ensure the ReturnUrl has a fallback to the default installation directory, since if you accidentally hit the login page and login, nothing happens if there is no return url

* switch to DependsOnTargets to account for if this is the only target being run (we need node_modules installed)

* add UmbracoUrl property

* add taghelper to use asp-append-version on login static assets
This commit is contained in:
Jacob Overgaard
2023-06-01 08:50:21 +02:00
committed by GitHub
parent 33cb74dff1
commit 43ffeb673b
4 changed files with 63 additions and 86 deletions

1
.gitignore vendored
View File

@@ -73,6 +73,7 @@ preserve.belle
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/js
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/lib
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/views
/src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/login
# Environment specific data
/src/Umbraco.Web.UI.Client/[Bb]uild/

View File

@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
namespace Umbraco.Cms.Api.Management;
@@ -7,27 +9,45 @@ namespace Umbraco.Cms.Api.Management;
public class
BackOfficeLoginModel
{
/// <summary>
/// Gets or sets the value of the "ReturnUrl" query parameter or defaults to the configured Umbraco directory.
/// </summary>
[FromQuery(Name = "ReturnUrl")]
public string? ReturnUrl { get; set; }
public string AuthUrl { get; set; } = string.Empty;
/// <summary>
/// The configured Umbraco directory.
/// </summary>
public string? UmbracoUrl { get; set; }
}
[ApiExplorerSettings(IgnoreApi=true)]
[Route("/umbraco/login")]
public class BackOfficeLoginController : Controller
{
private readonly LinkGenerator _linkGenerator;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly GlobalSettings _globalSettings;
public BackOfficeLoginController(LinkGenerator linkGenerator)
public BackOfficeLoginController(
IOptionsSnapshot<GlobalSettings> globalSettings,
IHostingEnvironment hostingEnvironment)
{
_linkGenerator = linkGenerator;
_hostingEnvironment = hostingEnvironment;
_globalSettings = globalSettings.Value ?? throw new ArgumentNullException(nameof(globalSettings));
}
// GET
public IActionResult Index(BackOfficeLoginModel model)
{
model.AuthUrl = "/umbraco/management/api/v1.0/security/back-office";
if (string.IsNullOrEmpty(model.UmbracoUrl))
{
model.UmbracoUrl = _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoPath);
}
if (string.IsNullOrEmpty(model.ReturnUrl))
{
model.ReturnUrl = model.UmbracoUrl;
}
return View("/umbraco/UmbracoLogin/Index.cshtml", model);
}

View File

@@ -20,6 +20,7 @@
<BasePath>$(ProjectDir)wwwroot\umbraco</BasePath>
<BellePath>$(BasePath)\lib</BellePath>
<BackofficePath>$(BasePath)\backoffice</BackofficePath>
<LoginPath>$(BasePath)\login</LoginPath>
</PropertyGroup>
<Target Name="BuildBellePreconditions" BeforeTargets="Build">
@@ -32,6 +33,11 @@
<Message Text="Skip BuildBackOffice target because '$(BackofficePath)' already exists" Importance="high" Condition="Exists('$(BackofficePath)')" />
<Message Text="Call BuildBackOffice target because UmbracoBuild is empty (this is Visual Studio) and '$(BackofficePath)' doesn't exist" Importance="high" Condition="'$(UmbracoBuild)' == '' and !Exists('$(BackofficePath)')" />
<CallTarget Targets="BuildBackOffice" Condition="'$(UmbracoBuild)' == '' and !Exists('$(BackofficePath)')" />
<Message Text="Skip BuildLogin target because UmbracoBuild is '$(UmbracoBuild)' (this is not Visual Studio)" Importance="high" Condition="'$(UmbracoBuild)' != ''" />
<Message Text="Skip BuildLogin target because '$(LoginPath)' already exists" Importance="high" Condition="Exists('$(LoginPath)')" />
<Message Text="Call BuildLogin target because UmbracoBuild is empty (this is Visual Studio) and '$(LoginPath)' doesn't exist" Importance="high" Condition="'$(UmbracoBuild)' == '' and !Exists('$(LoginPath)')" />
<CallTarget Targets="BuildLogin" Condition="'$(UmbracoBuild)' == '' and !Exists('$(LoginPath)')" />
</Target>
<Target Name="BuildBelle">
@@ -44,6 +50,10 @@
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.New.Client\" Command="npm run build:for:cms" />
</Target>
<Target Name="BuildLogin" DependsOnTargets="BuildBackOffice">
<Exec WorkingDirectory="$(ProjectDir)..\Umbraco.Web.UI.New.Client\apps\auth" Command="npm run build" />
</Target>
<Target Name="CleanBellePreconditions" AfterTargets="Clean" Condition="'$(UmbracoBuild)' == ''">
<Message Text="Skip CleanBelle target because '$(BellePath)' doesn't exist" Importance="high" Condition="!Exists('$(BellePath)')" />
<Message Text="Skip CleanBelle target because preserve.belle marker file exists" Importance="high" Condition="Exists('$(BellePath)') and Exists('$(SolutionDir)preserve.belle')" />
@@ -55,6 +65,11 @@
<Message Text="Call CleanBackoffice target because '$(BackofficePath)' exists and preserve.belle marker file doesn't exist" Importance="high" Condition="Exists('$(BackofficePath)') and !Exists('$(SolutionDir)preserve.belle')" />
<CallTarget Targets="CleanBackoffice" Condition="Exists('$(BackofficePath)') and !Exists('$(SolutionDir)preserve.belle')" />
<Message Text="Skip CleanLogin target because '$(LoginPath)' doesn't exist" Importance="high" Condition="!Exists('$(LoginPath)')" />
<Message Text="Skip CleanLogin target because preserve.belle marker file exists" Importance="high" Condition="Exists('$(LoginPath)') and Exists('$(SolutionDir)preserve.belle')" />
<Message Text="Call CleanLogin target because '$(LoginPath)' exists and preserve.belle marker file doesn't exist" Importance="high" Condition="Exists('$(LoginPath)') and !Exists('$(SolutionDir)preserve.belle')" />
<CallTarget Targets="CleanLogin" Condition="Exists('$(LoginPath)') and !Exists('$(SolutionDir)preserve.login')" />
</Target>
@@ -71,4 +86,11 @@
</ItemGroup>
<RemoveDir Directories="@(BackofficeDirectories)" />
</Target>
<Target Name="CleanLogin">
<ItemGroup>
<LoginDirectories Include="$(LoginPath);" />
</ItemGroup>
<RemoveDir Directories="@(LoginDirectories)" />
</Target>
</Project>

View File

@@ -1,85 +1,19 @@
@model Umbraco.Cms.Api.Management.BackOfficeLoginModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<title>Umbraco</title>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="~/umbraco/login/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Umbraco</title>
<script type="module" src="~/umbraco/login/main.js" asp-append-version="true"></script>
<link rel="stylesheet" href="~/umbraco/login/style.css" asp-append-version="true" />
<base href="@Model.UmbracoUrl/login/" />
</head>
<link rel="stylesheet" href="https://unpkg.com/blocks.css/dist/blocks.min.css"/>
<style>
body {
display: grid;
place-content: center;
justify-content: center;
align-items: center;
width: 100%;
height: 100vh;
}
.card {
display: grid;
place-items: center;
}
</style>
</head>
<body>
<div class="card fixed block">
<h1>Umbraco login universe</h1>
<p>We have not yet implemented the new login page, so for your pleasure,<br/>we present you a blocky experience that just works™</p>
<form id="loginform" method="post">
<div>
<label>Username: <input class="round block" type="text" name="username" autocomplete="username"></label>
</div>
<div>
<label>Password: <input class="round block" type="password" name="password" autocomplete="current-password"></label>
</div>
<button id="login" class="accent block">LOGIN</button>
</form>
</div>
<script>
!(function () {
const authUrlLogin = "@Model.AuthUrl/login"
const returnUrl = "@Model.ReturnUrl".replaceAll("&amp;", "&")
console.log('urls', {authUrlLogin, returnUrl})
const form = document.getElementById("loginform");
form.addEventListener('submit', async (e) => {
e.preventDefault()
try {
const formData = new FormData(e.target)
const userName = formData.get("username")
const password = formData.get("password")
console.log('username', userName)
const res = await fetch(authUrlLogin, {
method: "POST",
body: JSON.stringify({
userName,
password
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
const content = await res.text()
console.log('login success', content)
if (returnUrl) {
location.href = returnUrl
}
} catch (err) {
alert('Could not login: ' + err.message)
throw err
}
});
})();
</script>
</body>
<body class="uui-font uui-text" style="margin: 0; padding: 0; overflow: hidden">
<umb-login return-url="@Model.ReturnUrl"></umb-login>
</body>
</html>