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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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("&", "&")
|
||||
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user