diff --git a/Squidex.sln b/Squidex.sln index bd07f14e5..1c40c8524 100644 --- a/Squidex.sln +++ b/Squidex.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 +VisualStudioVersion = 15.0.27004.2002 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex", "src\Squidex\Squidex.csproj", "{61F6BBCE-A080-4400-B194-70E2F5D2096E}" EndProject diff --git a/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj b/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj index 73c1db950..721bd7392 100644 --- a/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj +++ b/src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj index 2b809030a..af764763d 100644 --- a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj +++ b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj b/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj index 951f9fcbe..ca4434061 100644 --- a/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj +++ b/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj b/src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj index 79f4eaf26..95685bf46 100644 --- a/src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj +++ b/src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj b/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj index 7f9ebe67f..3e95a2ab6 100644 --- a/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj +++ b/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj b/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj index e0d2ec803..11b5070ec 100644 --- a/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj +++ b/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj b/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj index a3687426d..c06c834c2 100644 --- a/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj +++ b/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj @@ -8,7 +8,7 @@ True - + diff --git a/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index 0bdb3b4d4..c338c9865 100644 --- a/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -8,10 +8,10 @@ True - - + + - + diff --git a/src/Squidex/Config/Identity/AuthenticationServices.cs b/src/Squidex/Config/Identity/AuthenticationServices.cs new file mode 100644 index 000000000..aeb2743b9 --- /dev/null +++ b/src/Squidex/Config/Identity/AuthenticationServices.cs @@ -0,0 +1,62 @@ +// ========================================================================== +// AuthenticationServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Infrastructure; + +namespace Squidex.Config.Identity +{ + public static class AuthenticationServices + { + public static IServiceCollection AddMyAuthentication(this IServiceCollection services, IConfiguration configuration) + { + var identityOptions = configuration.GetSection("identity").Get(); + + services.AddAuthentication() + .AddMyGoogleAuthentication(identityOptions) + .AddMyMicrosoftAuthentication(identityOptions) + .AddMyApiProtection(identityOptions, configuration); + + return services; + } + + public static AuthenticationBuilder AddMyApiProtection(this AuthenticationBuilder authBuilder, MyIdentityOptions identityOptions, IConfiguration configuration) + { + var apiScope = Constants.ApiScope; + + var urlsOptions = configuration.GetSection("urls").Get(); + + if (!string.IsNullOrWhiteSpace(urlsOptions.BaseUrl)) + { + string apiAuthorityUrl; + + if (!string.IsNullOrWhiteSpace(identityOptions.AuthorityUrl)) + { + apiAuthorityUrl = identityOptions.AuthorityUrl.BuildFullUrl(Constants.IdentityPrefix); + } + else + { + apiAuthorityUrl = urlsOptions.BuildUrl(Constants.IdentityPrefix); + } + + authBuilder.AddIdentityServerAuthentication(options => + { + options.Authority = apiAuthorityUrl; + options.ApiName = apiScope; + options.ApiSecret = null; + options.RequireHttpsMetadata = identityOptions.RequiresHttps; + }); + } + + return authBuilder; + } + } +} diff --git a/src/Squidex/Config/Identity/AuthenticationUsage.cs b/src/Squidex/Config/Identity/AuthenticationUsage.cs new file mode 100644 index 000000000..b9dd4ad8f --- /dev/null +++ b/src/Squidex/Config/Identity/AuthenticationUsage.cs @@ -0,0 +1,22 @@ +// ========================================================================== +// AuthenticationUsage.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Microsoft.AspNetCore.Builder; + +namespace Squidex.Config.Identity +{ + public static class AuthenticationUsage + { + public static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app) + { + app.UseAuthentication(); + + return app; + } + } +} diff --git a/src/Squidex/Config/Identity/GithubHandler.cs b/src/Squidex/Config/Identity/GithubHandler.cs deleted file mode 100644 index a1e1cb433..000000000 --- a/src/Squidex/Config/Identity/GithubHandler.cs +++ /dev/null @@ -1,42 +0,0 @@ -// ========================================================================== -// GithubHandler.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System.Security.Claims; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication.OAuth; -using Squidex.Shared.Identity; - -namespace Squidex.Config.Identity -{ - public sealed class GitHubHandler : OAuthEvents - { - public override Task CreatingTicket(OAuthCreatingTicketContext context) - { - var userLogin = context.User.Value("login"); - var userName = context.User.Value("name"); - - if (!string.IsNullOrEmpty(userName)) - { - context.Identity.AddClaim(new Claim(SquidexClaimTypes.SquidexDisplayName, userName)); - } - else if (!string.IsNullOrWhiteSpace(userLogin)) - { - context.Identity.AddClaim(new Claim(SquidexClaimTypes.SquidexDisplayName, userName)); - } - - var pictureUrl = context.User.Value("avatar_url"); - - if (!string.IsNullOrEmpty(pictureUrl)) - { - context.Identity.AddClaim(new Claim(SquidexClaimTypes.SquidexPictureUrl, pictureUrl)); - } - - return base.CreatingTicket(context); - } - } -} diff --git a/src/Squidex/Config/Identity/GithubIdentityUsage.cs b/src/Squidex/Config/Identity/GithubIdentityUsage.cs deleted file mode 100644 index 022a2a4b7..000000000 --- a/src/Squidex/Config/Identity/GithubIdentityUsage.cs +++ /dev/null @@ -1,37 +0,0 @@ -// ========================================================================== -// GithubIdentityUsage.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using AspNet.Security.OAuth.GitHub; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Squidex.Config.Identity -{ - public static class GitHubIdentityUsage - { - public static IApplicationBuilder UseMyGithubAuthentication(this IApplicationBuilder app) - { - var options = app.ApplicationServices.GetService>().Value; - - if (options.IsGithubAuthConfigured()) - { - var githubOptions = - new GitHubAuthenticationOptions - { - ClientId = options.GithubClient, - ClientSecret = options.GithubSecret - }; - - app.UseGitHubAuthentication(githubOptions); - } - - return app; - } - } -} diff --git a/src/Squidex/Config/Identity/GoogleAuthenticationServices.cs b/src/Squidex/Config/Identity/GoogleAuthenticationServices.cs new file mode 100644 index 000000000..43dd42477 --- /dev/null +++ b/src/Squidex/Config/Identity/GoogleAuthenticationServices.cs @@ -0,0 +1,31 @@ +// ========================================================================== +// GoogleAuthenticationServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.DependencyInjection; + +namespace Squidex.Config.Identity +{ + public static class GoogleAuthenticationServices + { + public static AuthenticationBuilder AddMyGoogleAuthentication(this AuthenticationBuilder authBuilder, MyIdentityOptions identityOptions) + { + if (identityOptions.IsGoogleAuthConfigured()) + { + authBuilder.AddGoogle(options => + { + options.ClientId = identityOptions.GoogleClient; + options.ClientSecret = identityOptions.GoogleSecret; + options.Events = new GoogleHandler(); + }); + } + + return authBuilder; + } + } +} diff --git a/src/Squidex/Config/Identity/GoogleHandler.cs b/src/Squidex/Config/Identity/GoogleHandler.cs index 6c311dc34..df6f8c0f7 100644 --- a/src/Squidex/Config/Identity/GoogleHandler.cs +++ b/src/Squidex/Config/Identity/GoogleHandler.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Security.Claims; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.OAuth; using Squidex.Infrastructure.Tasks; using Squidex.Shared.Identity; @@ -17,7 +18,7 @@ namespace Squidex.Config.Identity { public sealed class GoogleHandler : OAuthEvents { - public override Task RedirectToAuthorizationEndpoint(OAuthRedirectToAuthorizationContext context) + public override Task RedirectToAuthorizationEndpoint(RedirectContext context) { context.Response.Redirect(context.RedirectUri + "&prompt=select_account"); diff --git a/src/Squidex/Config/Identity/GoogleIdentityUsage.cs b/src/Squidex/Config/Identity/GoogleIdentityUsage.cs deleted file mode 100644 index 78e68baa4..000000000 --- a/src/Squidex/Config/Identity/GoogleIdentityUsage.cs +++ /dev/null @@ -1,37 +0,0 @@ -// ========================================================================== -// GoogleIdentityUsage.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Squidex.Config.Identity -{ - public static class GoogleIdentityUsage - { - public static IApplicationBuilder UseMyGoogleAuthentication(this IApplicationBuilder app) - { - var options = app.ApplicationServices.GetService>().Value; - - if (options.IsGoogleAuthConfigured()) - { - var googleOptions = - new GoogleOptions - { - ClientId = options.GoogleClient, - ClientSecret = options.GoogleSecret, - Events = new GoogleHandler() - }; - - app.UseGoogleAuthentication(googleOptions); - } - - return app; - } - } -} diff --git a/src/Squidex/Config/Identity/IdentityServices.cs b/src/Squidex/Config/Identity/IdentityServices.cs index 5437166e8..cdd8421a2 100644 --- a/src/Squidex/Config/Identity/IdentityServices.cs +++ b/src/Squidex/Config/Identity/IdentityServices.cs @@ -113,7 +113,8 @@ namespace Squidex.Config.Identity public static IServiceCollection AddMyIdentity(this IServiceCollection services) { - services.AddIdentity().AddDefaultTokenProviders(); + services.AddIdentity() + .AddDefaultTokenProviders(); return services; } diff --git a/src/Squidex/Config/Identity/IdentityUsage.cs b/src/Squidex/Config/Identity/IdentityUsage.cs index 960eafd27..22db07e35 100644 --- a/src/Squidex/Config/Identity/IdentityUsage.cs +++ b/src/Squidex/Config/Identity/IdentityUsage.cs @@ -14,7 +14,6 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Squidex.Domain.Users; -using Squidex.Infrastructure; using Squidex.Infrastructure.Log; using Squidex.Shared.Identity; using Squidex.Shared.Users; @@ -23,13 +22,6 @@ namespace Squidex.Config.Identity { public static class IdentityUsage { - public static IApplicationBuilder UseMyIdentity(this IApplicationBuilder app) - { - app.UseIdentity(); - - return app; - } - public static IApplicationBuilder UseMyIdentityServer(this IApplicationBuilder app) { app.UseIdentityServer(); @@ -83,38 +75,5 @@ namespace Squidex.Config.Identity return app; } - - public static IApplicationBuilder UseMyApiProtection(this IApplicationBuilder app) - { - var apiScope = Constants.ApiScope; - - var urlsOptions = app.ApplicationServices.GetService>().Value; - - if (!string.IsNullOrWhiteSpace(urlsOptions.BaseUrl)) - { - var identityOptions = app.ApplicationServices.GetService>().Value; - - string apiAuthorityUrl; - - if (!string.IsNullOrWhiteSpace(identityOptions.AuthorityUrl)) - { - apiAuthorityUrl = identityOptions.AuthorityUrl.BuildFullUrl(Constants.IdentityPrefix); - } - else - { - apiAuthorityUrl = urlsOptions.BuildUrl(Constants.IdentityPrefix); - } - - app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions - { - Authority = apiAuthorityUrl, - ApiName = apiScope, - ApiSecret = null, - RequireHttpsMetadata = identityOptions.RequiresHttps - }); - } - - return app; - } } } diff --git a/src/Squidex/Config/Identity/MicrosoftAuthenticationServices.cs b/src/Squidex/Config/Identity/MicrosoftAuthenticationServices.cs new file mode 100644 index 000000000..b2ceeeaaf --- /dev/null +++ b/src/Squidex/Config/Identity/MicrosoftAuthenticationServices.cs @@ -0,0 +1,31 @@ +// ========================================================================== +// MicrosoftAuthenticationServices.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.DependencyInjection; + +namespace Squidex.Config.Identity +{ + public static class MicrosoftAuthenticationServices + { + public static AuthenticationBuilder AddMyMicrosoftAuthentication(this AuthenticationBuilder authBuilder, MyIdentityOptions identityOptions) + { + if (identityOptions.IsMicrosoftAuthConfigured()) + { + authBuilder.AddMicrosoftAccount(options => + { + options.ClientId = identityOptions.MicrosoftClient; + options.ClientSecret = identityOptions.MicrosoftSecret; + options.Events = new MicrosoftHandler(); + }); + } + + return authBuilder; + } + } +} diff --git a/src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs b/src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs deleted file mode 100644 index ab9c68358..000000000 --- a/src/Squidex/Config/Identity/MicrosoftIdentityUsage.cs +++ /dev/null @@ -1,37 +0,0 @@ -// ========================================================================== -// MicrosoftIdentityUsage.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Squidex.Config.Identity -{ - public static class MicrosoftIdentityUsage - { - public static IApplicationBuilder UseMyMicrosoftAuthentication(this IApplicationBuilder app) - { - var options = app.ApplicationServices.GetService>().Value; - - if (options.IsMicrosoftAuthConfigured()) - { - var googleOptions = - new MicrosoftAccountOptions - { - ClientId = options.MicrosoftClient, - ClientSecret = options.MicrosoftSecret, - Events = new MicrosoftHandler() - }; - - app.UseMicrosoftAccountAuthentication(googleOptions); - } - - return app; - } - } -} diff --git a/src/Squidex/Config/Identity/MyIdentityOptions.cs b/src/Squidex/Config/Identity/MyIdentityOptions.cs index ae8383df3..f38566b94 100644 --- a/src/Squidex/Config/Identity/MyIdentityOptions.cs +++ b/src/Squidex/Config/Identity/MyIdentityOptions.cs @@ -18,10 +18,6 @@ namespace Squidex.Config.Identity public string GoogleSecret { get; set; } - public string GithubClient { get; set; } - - public string GithubSecret { get; set; } - public string MicrosoftClient { get; set; } public string MicrosoftSecret { get; set; } @@ -39,11 +35,6 @@ namespace Squidex.Config.Identity return !string.IsNullOrWhiteSpace(AdminEmail) && !string.IsNullOrWhiteSpace(AdminPassword); } - public bool IsGithubAuthConfigured() - { - return !string.IsNullOrWhiteSpace(GithubClient) && !string.IsNullOrWhiteSpace(GithubSecret); - } - public bool IsGoogleAuthConfigured() { return !string.IsNullOrWhiteSpace(GoogleClient) && !string.IsNullOrWhiteSpace(GoogleSecret); diff --git a/src/Squidex/Controllers/Api/Apps/AppsController.cs b/src/Squidex/Controllers/Api/Apps/AppsController.cs index 447e31768..e1a39b542 100644 --- a/src/Squidex/Controllers/Api/Apps/AppsController.cs +++ b/src/Squidex/Controllers/Api/Apps/AppsController.cs @@ -9,7 +9,6 @@ using System; using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using NSwag.Annotations; using Squidex.Controllers.Api.Apps.Models; @@ -25,7 +24,7 @@ namespace Squidex.Controllers.Api.Apps /// /// Manages and configures apps. /// - [Authorize] + [ApiAuthorize] [ApiExceptionFilter] [SwaggerTag(nameof(Apps))] public sealed class AppsController : ControllerBase diff --git a/src/Squidex/Controllers/Api/Languages/LanguagesController.cs b/src/Squidex/Controllers/Api/Languages/LanguagesController.cs index aac500468..f87546cb1 100644 --- a/src/Squidex/Controllers/Api/Languages/LanguagesController.cs +++ b/src/Squidex/Controllers/Api/Languages/LanguagesController.cs @@ -7,7 +7,6 @@ // ========================================================================== using System.Linq; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using NSwag.Annotations; using Squidex.Infrastructure; @@ -19,7 +18,7 @@ namespace Squidex.Controllers.Api.Languages /// /// Readonly API to the supported langauges. /// - [Authorize] + [ApiAuthorize] [ApiExceptionFilter] [SwaggerTag(nameof(Languages))] public sealed class LanguagesController : Controller diff --git a/src/Squidex/Controllers/Api/Users/UsersController.cs b/src/Squidex/Controllers/Api/Users/UsersController.cs index c517780f9..5ec95aedd 100644 --- a/src/Squidex/Controllers/Api/Users/UsersController.cs +++ b/src/Squidex/Controllers/Api/Users/UsersController.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Net.Http; using System.Reflection; using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using NSwag.Annotations; @@ -62,7 +61,7 @@ namespace Squidex.Controllers.Api.Users /// /// 200 => Users returned. /// - [Authorize] + [ApiAuthorize] [HttpGet] [Route("users/")] [ProducesResponseType(typeof(UserDto[]), 200)] @@ -83,7 +82,7 @@ namespace Squidex.Controllers.Api.Users /// 200 => User found. /// 404 => User not found. /// - [Authorize] + [ApiAuthorize] [HttpGet] [Route("users/{id}/")] [ProducesResponseType(typeof(UserDto), 200)] diff --git a/src/Squidex/Controllers/UI/Account/AccountController.cs b/src/Squidex/Controllers/UI/Account/AccountController.cs index 2785404b4..952c33452 100644 --- a/src/Squidex/Controllers/UI/Account/AccountController.cs +++ b/src/Squidex/Controllers/UI/Account/AccountController.cs @@ -129,16 +129,16 @@ namespace Squidex.Controllers.UI.Account [HttpGet] [Route("account/signup/")] - public IActionResult Signup(string returnUrl = null) + public Task Signup(string returnUrl = null) { - return LoginView(returnUrl, false, false); + return LoginViewAsync(returnUrl, false, false); } [HttpGet] [Route("account/login/")] - public IActionResult Login(string returnUrl = null) + public Task Login(string returnUrl = null) { - return LoginView(returnUrl, true, false); + return LoginViewAsync(returnUrl, true, false); } [HttpPost] @@ -147,14 +147,14 @@ namespace Squidex.Controllers.UI.Account { if (!ModelState.IsValid) { - return LoginView(returnUrl, true, true); + return await LoginViewAsync(returnUrl, true, true); } var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, true, true); if (!result.Succeeded) { - return LoginView(returnUrl, true, true); + return await LoginViewAsync(returnUrl, true, true); } else if (!string.IsNullOrWhiteSpace(returnUrl)) { @@ -166,21 +166,20 @@ namespace Squidex.Controllers.UI.Account } } - private IActionResult LoginView(string returnUrl, bool isLogin, bool isFailed) + private async Task LoginViewAsync(string returnUrl, bool isLogin, bool isFailed) { var allowPasswordAuth = identityOptions.Value.AllowPasswordAuth; - var providers = - signInManager.GetExternalAuthenticationSchemes() - .Select(x => new ExternalProvider(x.AuthenticationScheme, x.DisplayName)).ToList(); + var externalSchemes = await signInManager.GetExternalAuthenticationSchemesAsync(); + var externalProviders = externalSchemes.Select(x => new ExternalProvider(x.Name, x.DisplayName)).ToList(); var vm = new LoginVM { - ExternalProviders = providers, + ExternalProviders = externalProviders, IsLogin = isLogin, IsFailed = isFailed, HasPasswordAuth = allowPasswordAuth, - HasPasswordAndExternal = allowPasswordAuth && providers.Any(), + HasPasswordAndExternal = allowPasswordAuth && externalProviders.Any(), ReturnUrl = returnUrl }; diff --git a/src/Squidex/Controllers/UI/Profile/ProfileController.cs b/src/Squidex/Controllers/UI/Profile/ProfileController.cs index a37f3c9ad..e09e105cd 100644 --- a/src/Squidex/Controllers/UI/Profile/ProfileController.cs +++ b/src/Squidex/Controllers/UI/Profile/ProfileController.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; @@ -34,19 +35,16 @@ namespace Squidex.Controllers.UI.Profile private readonly IUserPictureStore userPictureStore; private readonly IAssetThumbnailGenerator assetThumbnailGenerator; private readonly IOptions identityOptions; - private readonly IOptions identityCookieOptions; public ProfileController( SignInManager signInManager, UserManager userManager, IUserPictureStore userPictureStore, IAssetThumbnailGenerator assetThumbnailGenerator, - IOptions identityOptions, - IOptions identityCookieOptions) + IOptions identityOptions) { this.signInManager = signInManager; this.identityOptions = identityOptions; - this.identityCookieOptions = identityCookieOptions; this.userManager = userManager; this.userPictureStore = userPictureStore; this.assetThumbnailGenerator = assetThumbnailGenerator; @@ -65,7 +63,7 @@ namespace Squidex.Controllers.UI.Profile [Route("/account/profile/login-add/")] public async Task AddLogin(string provider) { - await HttpContext.Authentication.SignOutAsync(identityCookieOptions.Value.ExternalCookieAuthenticationScheme); + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, @@ -182,9 +180,8 @@ namespace Squidex.Controllers.UI.Profile private async Task GetProfileVM(IUser user, ChangeProfileModel model = null, string errorMessage = null, string successMessage = null) { - var providers = - signInManager.GetExternalAuthenticationSchemes() - .Select(x => new ExternalProvider(x.AuthenticationScheme, x.DisplayName)).ToList(); + var externalSchemes = await signInManager.GetExternalAuthenticationSchemesAsync(); + var externalProviders = externalSchemes.Select(x => new ExternalProvider(x.Name, x.DisplayName)).ToList(); var result = new ProfileVM { @@ -192,7 +189,7 @@ namespace Squidex.Controllers.UI.Profile Email = user.Email, ErrorMessage = errorMessage, ExternalLogins = user.Logins, - ExternalProviders = providers, + ExternalProviders = externalProviders, DisplayName = user.DisplayName(), HasPassword = await userManager.HasPasswordAsync(user), HasPasswordAuth = identityOptions.Value.AllowPasswordAuth, diff --git a/src/Squidex/Pipeline/ApiAuthorizeAttribute.cs b/src/Squidex/Pipeline/ApiAuthorizeAttribute.cs new file mode 100644 index 000000000..79d8be724 --- /dev/null +++ b/src/Squidex/Pipeline/ApiAuthorizeAttribute.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// ApiAuthorizeAttribute.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using IdentityServer4.AccessTokenValidation; +using Microsoft.AspNetCore.Authorization; + +namespace Squidex.Pipeline +{ + public class ApiAuthorizeAttribute : AuthorizeAttribute + { + public ApiAuthorizeAttribute() + { + AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme; + } + } +} diff --git a/src/Squidex/Pipeline/FileCallbackResultExecutor.cs b/src/Squidex/Pipeline/FileCallbackResultExecutor.cs index 355d0f48c..d607dbc8c 100644 --- a/src/Squidex/Pipeline/FileCallbackResultExecutor.cs +++ b/src/Squidex/Pipeline/FileCallbackResultExecutor.cs @@ -25,7 +25,7 @@ namespace Squidex.Pipeline { try { - SetHeadersAndLog(context, result); + SetHeadersAndLog(context, result, null); await result.Callback(context.HttpContext.Response.Body); } diff --git a/src/Squidex/Pipeline/MustBeAdministratorAttribute.cs b/src/Squidex/Pipeline/MustBeAdministratorAttribute.cs index fa9a0ba76..ddb71bcc9 100644 --- a/src/Squidex/Pipeline/MustBeAdministratorAttribute.cs +++ b/src/Squidex/Pipeline/MustBeAdministratorAttribute.cs @@ -6,12 +6,11 @@ // All rights reserved. // ========================================================================== -using Microsoft.AspNetCore.Authorization; using Squidex.Shared.Identity; namespace Squidex.Pipeline { - public sealed class MustBeAdministratorAttribute : AuthorizeAttribute + public sealed class MustBeAdministratorAttribute : ApiAuthorizeAttribute { public MustBeAdministratorAttribute() { diff --git a/src/Squidex/Pipeline/MustBeAppDeveloperAttribute.cs b/src/Squidex/Pipeline/MustBeAppDeveloperAttribute.cs index 28ce12b41..5b3dc042a 100644 --- a/src/Squidex/Pipeline/MustBeAppDeveloperAttribute.cs +++ b/src/Squidex/Pipeline/MustBeAppDeveloperAttribute.cs @@ -6,12 +6,11 @@ // All rights reserved. // ========================================================================== -using Microsoft.AspNetCore.Authorization; using Squidex.Shared.Identity; namespace Squidex.Pipeline { - public sealed class MustBeAppDeveloperAttribute : AuthorizeAttribute + public sealed class MustBeAppDeveloperAttribute : ApiAuthorizeAttribute { public MustBeAppDeveloperAttribute() { diff --git a/src/Squidex/Pipeline/MustBeAppEditorAttribute.cs b/src/Squidex/Pipeline/MustBeAppEditorAttribute.cs index 965cf123d..443bf2d12 100644 --- a/src/Squidex/Pipeline/MustBeAppEditorAttribute.cs +++ b/src/Squidex/Pipeline/MustBeAppEditorAttribute.cs @@ -6,12 +6,11 @@ // All rights reserved. // ========================================================================== -using Microsoft.AspNetCore.Authorization; using Squidex.Shared.Identity; namespace Squidex.Pipeline { - public sealed class MustBeAppEditorAttribute : AuthorizeAttribute + public sealed class MustBeAppEditorAttribute : ApiAuthorizeAttribute { public MustBeAppEditorAttribute() { diff --git a/src/Squidex/Pipeline/MustBeAppOwnerAttribute.cs b/src/Squidex/Pipeline/MustBeAppOwnerAttribute.cs index 20ad0e605..b3e069ca4 100644 --- a/src/Squidex/Pipeline/MustBeAppOwnerAttribute.cs +++ b/src/Squidex/Pipeline/MustBeAppOwnerAttribute.cs @@ -6,12 +6,11 @@ // All rights reserved. // ========================================================================== -using Microsoft.AspNetCore.Authorization; using Squidex.Shared.Identity; namespace Squidex.Pipeline { - public sealed class MustBeAppOwnerAttribute : AuthorizeAttribute + public sealed class MustBeAppOwnerAttribute : ApiAuthorizeAttribute { public MustBeAppOwnerAttribute() { diff --git a/src/Squidex/Pipeline/MustBeAppReaderAttribute.cs b/src/Squidex/Pipeline/MustBeAppReaderAttribute.cs index 2ca5282a3..d18c94c56 100644 --- a/src/Squidex/Pipeline/MustBeAppReaderAttribute.cs +++ b/src/Squidex/Pipeline/MustBeAppReaderAttribute.cs @@ -6,12 +6,11 @@ // All rights reserved. // ========================================================================== -using Microsoft.AspNetCore.Authorization; using Squidex.Shared.Identity; namespace Squidex.Pipeline { - public sealed class MustBeAppReaderAttribute : AuthorizeAttribute + public sealed class MustBeAppReaderAttribute : ApiAuthorizeAttribute { public MustBeAppReaderAttribute() { diff --git a/src/Squidex/Program.cs b/src/Squidex/Program.cs index 783accd70..35d74a6e0 100644 --- a/src/Squidex/Program.cs +++ b/src/Squidex/Program.cs @@ -8,6 +8,7 @@ using System.IO; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; namespace Squidex { @@ -20,6 +21,14 @@ namespace Squidex .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup() + .ConfigureAppConfiguration((hostContext, options) => + { + options.Sources.Clear(); + options.AddJsonFile("appsettings.json", true, true); + options.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", true); + options.AddEnvironmentVariables(); + options.AddCommandLine(args); + }) .Build() .Run(); } diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index 91a6c2bc7..ba48b4a0f 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -15,9 +15,14 @@ + + + + + PreserveNewest @@ -42,43 +47,33 @@ - - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - + - + - + - + diff --git a/src/Squidex/Startup.cs b/src/Squidex/Startup.cs index c2a42f306..deb1601d0 100644 --- a/src/Squidex/Startup.cs +++ b/src/Squidex/Startup.cs @@ -25,6 +25,8 @@ using Squidex.Config.Web; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log.Adapter; +#pragma warning disable RECS0002 // Convert anonymous method to method group + namespace Squidex { public class Startup @@ -37,21 +39,15 @@ namespace Squidex "/error" }; - private IConfigurationRoot Configuration { get; } + private IConfiguration Configuration { get; } private IHostingEnvironment Environment { get; } - public Startup(IHostingEnvironment env) + public Startup(IHostingEnvironment env, IConfiguration config) { Environment = env; - var builder = new ConfigurationBuilder() - .SetBasePath(env.ContentRootPath) - .AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true) - .AddEnvironmentVariables(); - - Configuration = builder.Build(); + Configuration = config; } public IServiceProvider ConfigureServices(IServiceCollection services) @@ -59,6 +55,7 @@ namespace Squidex services.AddMySwaggerSettings(); services.AddMyEventFormatter(); services.AddMyDataProtectection(Configuration); + services.AddMyAuthentication(Configuration); services.AddMyIdentity(); services.AddMyIdentityServer(); services.AddMyMvc(); @@ -142,14 +139,10 @@ namespace Squidex identityApp.UseExceptionHandler("/error"); } - identityApp.UseMyIdentity(); + identityApp.UseMyAuthentication(); identityApp.UseMyIdentityServer(); identityApp.UseMyAdminRole(); identityApp.UseMyAdmin(); - identityApp.UseMyApiProtection(); - identityApp.UseMyGoogleAuthentication(); - identityApp.UseMyGithubAuthentication(); - identityApp.UseMyMicrosoftAuthentication(); identityApp.UseStaticFiles(); identityApp.MapWhen(x => IsIdentityRequest(x), mvcApp => @@ -169,7 +162,6 @@ namespace Squidex } appApi.UseMySwagger(); - appApi.UseMyApiProtection(); appApi.MapWhen(x => !IsIdentityRequest(x), mvcApp => { diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj b/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj index 7f3e5875b..e2b84e201 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj @@ -11,7 +11,6 @@ - diff --git a/tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj b/tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj index a46de5c85..24478e87f 100644 --- a/tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj b/tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj index d891202a4..19ac1d18e 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj b/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj index 13aa91f47..f54f04636 100644 --- a/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj +++ b/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj b/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj index 73b94a3c5..feee70e93 100644 --- a/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj +++ b/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj @@ -11,11 +11,11 @@ - + - - + + diff --git a/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs b/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs index 6259fd841..f91948809 100644 --- a/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs +++ b/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs @@ -114,7 +114,7 @@ namespace Squidex.Infrastructure.UsageTracking sut.Next(); sut.Dispose(); - A.CallTo(() => usageStore.TrackUsagesAsync(A.Ignored, A.Ignored, A.Ignored, A.Ignored)).MustNotHaveHappened(); + A.CallTo(() => usageStore.TrackUsagesAsync(A.Ignored, A.Ignored, A.Ignored, A.Ignored)).MustNotHaveHappened(); } [Fact]