From 7efb337ae653a1e206bb46a1371256b17feee245 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 18 Nov 2017 22:16:34 +0100 Subject: [PATCH] Frontend Area. --- .../Middlewares}/WebpackMiddleware.cs | 2 +- src/Squidex/Areas/Frontend/Startup.cs | 81 +++++++++++++++++++ ...rleansDashboardAuthenticationMiddleware.cs | 42 ++++++++++ src/Squidex/Areas/OrleansDashboard/Startup.cs | 27 +++++++ src/Squidex/Config/Constants.cs | 6 ++ .../Config/Identity/AuthenticationServices.cs | 13 +++ .../Config/Identity/LazyClientStore.cs | 36 +++++++-- src/Squidex/Config/Orleans/SiloExtensions.cs | 27 ------- src/Squidex/Config/Web/WebExtensions.cs | 32 -------- src/Squidex/Config/Web/WebpackExtensions.cs | 7 -- .../UI/Account/AccountController.cs | 14 ---- src/Squidex/Program.cs | 2 +- src/Squidex/WebStartup.cs | 49 ++--------- .../app/shared/services/auth.service.ts | 4 +- .../wwwroot/client-callback-popup.html | 12 +++ .../wwwroot/client-callback-silent.html | 12 +++ 16 files changed, 233 insertions(+), 133 deletions(-) rename src/Squidex/{Pipeline => Areas/Frontend/Middlewares}/WebpackMiddleware.cs (98%) create mode 100644 src/Squidex/Areas/Frontend/Startup.cs create mode 100644 src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs create mode 100644 src/Squidex/Areas/OrleansDashboard/Startup.cs create mode 100644 src/Squidex/wwwroot/client-callback-popup.html create mode 100644 src/Squidex/wwwroot/client-callback-silent.html diff --git a/src/Squidex/Pipeline/WebpackMiddleware.cs b/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs similarity index 98% rename from src/Squidex/Pipeline/WebpackMiddleware.cs rename to src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs index 71f14245a..8a4686ecf 100644 --- a/src/Squidex/Pipeline/WebpackMiddleware.cs +++ b/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs @@ -11,7 +11,7 @@ using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -namespace Squidex.Pipeline +namespace Squidex.Areas.Frontend.Middlewares { public sealed class WebpackMiddleware { diff --git a/src/Squidex/Areas/Frontend/Startup.cs b/src/Squidex/Areas/Frontend/Startup.cs new file mode 100644 index 000000000..2dc954cc7 --- /dev/null +++ b/src/Squidex/Areas/Frontend/Startup.cs @@ -0,0 +1,81 @@ +// ========================================================================== +// Startup.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.IO; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; +using Squidex.Areas.Frontend.Middlewares; + +namespace Squidex.Areas.Frontend +{ + public static class Startup + { + public static void ConfigureFrontend(this IApplicationBuilder app) + { + var environment = app.ApplicationServices.GetRequiredService(); + + if (environment.IsDevelopment()) + { + app.UseMiddleware(); + } + + app.Use((context, next) => + { + if (context.Request.Path == "/client-callback-popup") + { + context.Request.Path = new PathString("/client-callback-popup.html"); + } + else if (context.Request.Path == "/client-callback-silent") + { + context.Request.Path = new PathString("/client-callback-silent.html"); + } + else if (!Path.HasExtension(context.Request.Path.Value)) + { + if (environment.IsDevelopment()) + { + context.Request.Path = new PathString("/index.html"); + } + else + { + context.Request.Path = new PathString("/build/index.html"); + } + } + + return next(); + }); + + app.UseStaticFiles(new StaticFileOptions + { + OnPrepareResponse = context => + { + var response = context.Context.Response; + var responseHeaders = response.GetTypedHeaders(); + + if (!string.Equals(response.ContentType, "text/html", StringComparison.OrdinalIgnoreCase)) + { + responseHeaders.CacheControl = new CacheControlHeaderValue + { + MaxAge = TimeSpan.FromDays(60) + }; + } + else + { + responseHeaders.CacheControl = new CacheControlHeaderValue + { + NoCache = true + }; + } + } + }); + } + } +} diff --git a/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs b/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs new file mode 100644 index 000000000..5b27821c5 --- /dev/null +++ b/src/Squidex/Areas/OrleansDashboard/Middlewares/OrleansDashboardAuthenticationMiddleware.cs @@ -0,0 +1,42 @@ +// ========================================================================== +// LazyClientStore.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Http; +using Squidex.Shared.Identity; + +namespace Squidex.Areas.OrleansDashboard.Middlewares +{ + public sealed class OrleansDashboardAuthenticationMiddleware + { + private readonly RequestDelegate next; + + public OrleansDashboardAuthenticationMiddleware(RequestDelegate next) + { + this.next = next; + } + + public async Task Invoke(HttpContext context) + { + var authentication = await context.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); + + if (!authentication.Succeeded || !authentication.Principal.IsInRole(SquidexRoles.Administrator)) + { + await context.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties + { + RedirectUri = context.Request.PathBase + context.Request.Path + }); + } + + await next(context); + } + } +} diff --git a/src/Squidex/Areas/OrleansDashboard/Startup.cs b/src/Squidex/Areas/OrleansDashboard/Startup.cs new file mode 100644 index 000000000..381117424 --- /dev/null +++ b/src/Squidex/Areas/OrleansDashboard/Startup.cs @@ -0,0 +1,27 @@ +// ========================================================================== +// Startup.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Microsoft.AspNetCore.Builder; +using Orleans; +using Squidex.Areas.OrleansDashboard.Middlewares; + +namespace Squidex.Areas.OrleansDashboard +{ + public static class Startup + { + public static void ConfigureOrleansDashboard(this IApplicationBuilder app) + { + app.Map("/orleans", orleansApp => + { + orleansApp.UseAuthentication(); + orleansApp.UseMiddleware(); + orleansApp.UseOrleansDashboard(); + }); + } + } +} diff --git a/src/Squidex/Config/Constants.cs b/src/Squidex/Config/Constants.cs index d14eeed22..80b4b505b 100644 --- a/src/Squidex/Config/Constants.cs +++ b/src/Squidex/Config/Constants.cs @@ -6,6 +6,8 @@ // All rights reserved. // ========================================================================== +using IdentityServer4.Models; + namespace Squidex.Config { public static class Constants @@ -22,6 +24,10 @@ namespace Squidex.Config public static readonly string FrontendClient = "squidex-frontend"; + public static readonly string InternalClientId = "squidex-internal"; + + public static readonly string InternalClientSecret = "squidex-internal".Sha256(); + public static readonly string IdentityPrefix = "/identity-server"; public static readonly string OrleansPrefix = "/orleans"; diff --git a/src/Squidex/Config/Identity/AuthenticationServices.cs b/src/Squidex/Config/Identity/AuthenticationServices.cs index 38cec3b7b..1b9420896 100644 --- a/src/Squidex/Config/Identity/AuthenticationServices.cs +++ b/src/Squidex/Config/Identity/AuthenticationServices.cs @@ -7,6 +7,7 @@ // ========================================================================== using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -21,6 +22,7 @@ namespace Squidex.Config.Identity var identityOptions = config.GetSection("identity").Get(); services.AddAuthentication() + .AddCookie() .AddMyGoogleAuthentication(identityOptions) .AddMyMicrosoftAuthentication(identityOptions) .AddMyApiProtection(identityOptions, config); @@ -52,6 +54,17 @@ namespace Squidex.Config.Identity options.ApiSecret = null; options.RequireHttpsMetadata = identityOptions.RequiresHttps; }); + + authBuilder.AddOpenIdConnect(options => + { + options.ClientId = Constants.InternalClientId; + options.ClientSecret = Constants.InternalClientSecret; + options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.Authority = apiAuthorityUrl; + options.RequireHttpsMetadata = identityOptions.RequiresHttps; + options.Scope.Add(Constants.RoleScope); + options.SaveTokens = true; + }); } return authBuilder; diff --git a/src/Squidex/Config/Identity/LazyClientStore.cs b/src/Squidex/Config/Identity/LazyClientStore.cs index 9aa0df67c..5a000674c 100644 --- a/src/Squidex/Config/Identity/LazyClientStore.cs +++ b/src/Squidex/Config/Identity/LazyClientStore.cs @@ -91,17 +91,17 @@ namespace Squidex.Config.Identity private static IEnumerable CreateStaticClients(MyUrlsOptions urlsOptions) { - var id = Constants.FrontendClient; + var frontendId = Constants.FrontendClient; yield return new Client { - ClientId = id, - ClientName = id, + ClientId = frontendId, + ClientName = frontendId, RedirectUris = new List { urlsOptions.BuildUrl("login;"), - urlsOptions.BuildUrl("identity-server/client-callback-silent/"), - urlsOptions.BuildUrl("identity-server/client-callback-popup/") + urlsOptions.BuildUrl("client-callback-silent", false), + urlsOptions.BuildUrl("client-callback-popup", false) }, PostLogoutRedirectUris = new List { @@ -120,6 +120,32 @@ namespace Squidex.Config.Identity }, RequireConsent = false }; + + var internalClient = Constants.InternalClientId; + + yield return new Client + { + ClientId = internalClient, + ClientName = internalClient, + ClientSecrets = new List { new Secret(Constants.InternalClientSecret) }, + RedirectUris = new List + { + urlsOptions.BuildUrl("orleans/signin-oidc", false), + urlsOptions.BuildUrl("portal/signin-oidc", false) + }, + AccessTokenLifetime = (int)TimeSpan.FromDays(30).TotalSeconds, + AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials, + AllowedScopes = new List + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + IdentityServerConstants.StandardScopes.Email, + Constants.ApiScope, + Constants.ProfileScope, + Constants.RoleScope + }, + RequireConsent = false + }; } } } diff --git a/src/Squidex/Config/Orleans/SiloExtensions.cs b/src/Squidex/Config/Orleans/SiloExtensions.cs index db03aa84b..c58028dd8 100644 --- a/src/Squidex/Config/Orleans/SiloExtensions.cs +++ b/src/Squidex/Config/Orleans/SiloExtensions.cs @@ -7,13 +7,10 @@ // ========================================================================== using System.Reflection; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Orleans; using Orleans.Hosting; using Orleans.Runtime.Configuration; -using Squidex.Shared.Identity; namespace Squidex.Config.Orleans { @@ -42,29 +39,5 @@ namespace Squidex.Config.Orleans return config; } - - public static IApplicationBuilder UseMyOrleansDashboard(this IApplicationBuilder app) - { - app.Use(async (context, next) => - { - var authentication = await context.AuthenticateAsync(); - - if (authentication.Succeeded && authentication.Principal.IsInRole(SquidexRoles.Administrator)) - { - await next(); - } - else - { - await context.ChallengeAsync(new AuthenticationProperties - { - RedirectUri = context.Request.PathBase + context.Request.Path - }); - } - }); - - app.UseOrleansDashboard(); - - return app; - } } } diff --git a/src/Squidex/Config/Web/WebExtensions.cs b/src/Squidex/Config/Web/WebExtensions.cs index 314360c5a..ac1657f94 100644 --- a/src/Squidex/Config/Web/WebExtensions.cs +++ b/src/Squidex/Config/Web/WebExtensions.cs @@ -6,11 +6,8 @@ // All rights reserved. // ========================================================================== -using System; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.Net.Http.Headers; using Squidex.Pipeline; namespace Squidex.Config.Web @@ -37,34 +34,5 @@ namespace Squidex.Config.Web app.UseMiddleware(); } - - public static void UseMyCachedStaticFiles(this IApplicationBuilder app) - { - app.UseStaticFiles(new StaticFileOptions - { - OnPrepareResponse = context => - { - context.Context.Request.GetTypedHeaders(); - var response = context.Context.Response; - - var headers = response.GetTypedHeaders(); - - if (!string.Equals(response.ContentType, "text/html", StringComparison.OrdinalIgnoreCase)) - { - headers.CacheControl = new CacheControlHeaderValue - { - MaxAge = TimeSpan.FromDays(60) - }; - } - else - { - headers.CacheControl = new CacheControlHeaderValue - { - NoCache = true - }; - } - } - }); - } } } diff --git a/src/Squidex/Config/Web/WebpackExtensions.cs b/src/Squidex/Config/Web/WebpackExtensions.cs index b33a7979d..7474d8f50 100644 --- a/src/Squidex/Config/Web/WebpackExtensions.cs +++ b/src/Squidex/Config/Web/WebpackExtensions.cs @@ -13,13 +13,6 @@ namespace Squidex.Config.Web { public static class WebpackExtensions { - public static IApplicationBuilder UseWebpackProxy(this IApplicationBuilder app) - { - app.UseMiddleware(); - - return app; - } - public static IApplicationBuilder UseMyTracking(this IApplicationBuilder app) { app.UseMiddleware(); diff --git a/src/Squidex/Controllers/UI/Account/AccountController.cs b/src/Squidex/Controllers/UI/Account/AccountController.cs index 55ff820f6..3dbb53458 100644 --- a/src/Squidex/Controllers/UI/Account/AccountController.cs +++ b/src/Squidex/Controllers/UI/Account/AccountController.cs @@ -61,20 +61,6 @@ namespace Squidex.Controllers.UI.Account this.signInManager = signInManager; } - [HttpGet] - [Route("client-callback-silent/")] - public IActionResult ClientSilent() - { - return View(); - } - - [HttpGet] - [Route("client-callback-popup/")] - public IActionResult ClientPopup() - { - return View(); - } - [HttpGet] [Route("account/forbidden/")] public IActionResult Forbidden() diff --git a/src/Squidex/Program.cs b/src/Squidex/Program.cs index d0a45c6bc..eddaf6e8f 100644 --- a/src/Squidex/Program.cs +++ b/src/Squidex/Program.cs @@ -41,7 +41,7 @@ namespace Squidex }) .ConfigureLogging(builder => { - builder.AddSemanticLog(); + // builder.AddSemanticLog(); }) .ConfigureAppConfiguration((hostContext, builder) => { diff --git a/src/Squidex/WebStartup.cs b/src/Squidex/WebStartup.cs index 190d091c2..ea55eb3c4 100644 --- a/src/Squidex/WebStartup.cs +++ b/src/Squidex/WebStartup.cs @@ -7,14 +7,14 @@ // ========================================================================== using System; -using System.IO; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Orleans; +using Squidex.Areas.Frontend; +using Squidex.Areas.OrleansDashboard; using Squidex.Config; using Squidex.Config.Domain; using Squidex.Config.Identity; @@ -60,15 +60,13 @@ namespace Squidex app.UseMyCors(); app.UseMyForwardingRules(); app.UseMyTracking(); + app.UseMyAuthentication(); MapAndUseIdentityServer(app); MapAndUseApi(app); - MapAndUseOrleans(app); - MapAndUseFrontend(app); - } - private void MapAndUseOrleans(IApplicationBuilder app) - { + app.ConfigureOrleansDashboard(); + app.ConfigureFrontend(); } private void MapAndUseIdentityServer(IApplicationBuilder app) @@ -84,7 +82,6 @@ namespace Squidex identityApp.UseExceptionHandler("/error"); } - identityApp.UseMyAuthentication(); identityApp.UseMyIdentityServer(); identityApp.UseMyAdminRole(); identityApp.UseMyAdmin(); @@ -94,11 +91,6 @@ namespace Squidex { mvcApp.UseMvc(); }); - - identityApp.Map(Constants.OrleansPrefix, orleansApp => - { - orleansApp.UseMyOrleansDashboard(); - }); }); } @@ -120,37 +112,6 @@ namespace Squidex }); } - private void MapAndUseFrontend(IApplicationBuilder app) - { - if (environment.IsDevelopment()) - { - app.UseWebpackProxy(); - - app.Use((context, next) => - { - if (!Path.HasExtension(context.Request.Path.Value)) - { - context.Request.Path = new PathString("/index.html"); - } - return next(); - }); - } - else - { - app.Use((context, next) => - { - if (!Path.HasExtension(context.Request.Path.Value)) - { - context.Request.Path = new PathString("/build/index.html"); - } - - return next(); - }); - } - - app.UseMyCachedStaticFiles(); - } - private static bool IsIdentityRequest(HttpContext context) { return IdentityServerPaths.Any(p => context.Request.Path.StartsWithSegments(p)); diff --git a/src/Squidex/app/shared/services/auth.service.ts b/src/Squidex/app/shared/services/auth.service.ts index efdfb970a..372ffb7bb 100644 --- a/src/Squidex/app/shared/services/auth.service.ts +++ b/src/Squidex/app/shared/services/auth.service.ts @@ -79,8 +79,8 @@ export class AuthService { response_type: 'id_token token', redirect_uri: apiUrl.buildUrl('login;'), post_logout_redirect_uri: apiUrl.buildUrl('logout'), - silent_redirect_uri: apiUrl.buildUrl('identity-server/client-callback-silent/'), - popup_redirect_uri: apiUrl.buildUrl('identity-server/client-callback-popup/'), + silent_redirect_uri: apiUrl.buildUrl('client-callback-silent'), + popup_redirect_uri: apiUrl.buildUrl('client-callback-popup'), authority: apiUrl.buildUrl('identity-server/'), userStore: new WebStorageStateStore({ store: window.localStorage || window.sessionStorage }), automaticSilentRenew: true diff --git a/src/Squidex/wwwroot/client-callback-popup.html b/src/Squidex/wwwroot/client-callback-popup.html new file mode 100644 index 000000000..d2dee8683 --- /dev/null +++ b/src/Squidex/wwwroot/client-callback-popup.html @@ -0,0 +1,12 @@ + + + + + + + diff --git a/src/Squidex/wwwroot/client-callback-silent.html b/src/Squidex/wwwroot/client-callback-silent.html new file mode 100644 index 000000000..b1aefa168 --- /dev/null +++ b/src/Squidex/wwwroot/client-callback-silent.html @@ -0,0 +1,12 @@ + + + + + + +