diff --git a/docs/en/Best-Practices/Entity-Framework-Core-Integration.md b/docs/en/Best-Practices/Entity-Framework-Core-Integration.md index 04ddeaeba4..4db6518201 100644 --- a/docs/en/Best-Practices/Entity-Framework-Core-Integration.md +++ b/docs/en/Best-Practices/Entity-Framework-Core-Integration.md @@ -16,11 +16,13 @@ [ConnectionStringName("AbpIdentity")] public interface IIdentityDbContext : IEfCoreDbContext { - DbSet Users { get; set; } - DbSet Roles { get; set; } + DbSet Users { get; } + DbSet Roles { get; } } ```` +* **Do not** define `set;` for the properties in this interface. + ### DbContext class * **Do** inherit the `DbContext` from the `AbpDbContext` class. diff --git a/docs/en/Customizing-Application-Modules-Overriding-Services.md b/docs/en/Customizing-Application-Modules-Overriding-Services.md index ab6f29816b..081b142ad7 100644 --- a/docs/en/Customizing-Application-Modules-Overriding-Services.md +++ b/docs/en/Customizing-Application-Modules-Overriding-Services.md @@ -196,12 +196,16 @@ This example replaces the `AccountController` (An API Controller defined in the **`[ExposeServices(typeof(AccountController))]` is essential** here since it registers this controller for the `AccountController` in the dependency injection system. `[Dependency(ReplaceServices = true)]` is also recommended to clear the old registration (even the ASP.NET Core DI system selects the last registered one). -In addition, The `AccountController` will be removed from [`ApplicationModel`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.applicationmodels.applicationmodel.controllers) because it defines `ExposeServicesAttribute`. If you don't want to remove it, you can configure `AbpAspNetCoreMvcOptions`: +In addition, the `MyAccountController` will be removed from [`ApplicationModel`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.applicationmodels.applicationmodel.controllers) because it defines `ExposeServicesAttribute`. + +If `IncludeSelf = true` is specified, i.e. `[ExposeServices(typeof(AccountController), IncludeSelf = true)]`, then `AccountController` will be removed instead. This is useful for **extending** a controller. + +If you don't want to remove either controller, you can configure `AbpAspNetCoreMvcOptions`: ```csharp Configure(options => { - options.IgnoredControllersOnModelExclusion.AddIfNotContains(typeof(AccountController)); + options.IgnoredControllersOnModelExclusion.AddIfNotContains(typeof(MyAccountController)); }); ``` diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs index 4d1490ec40..996e495588 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs @@ -73,7 +73,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions protected virtual void RemoveDuplicateControllers(ApplicationModel application) { - var derivedControllerModels = new List(); + var controllerModelsToRemove = new List(); foreach (var controllerModel in application.Controllers) { @@ -87,19 +87,42 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions continue; } + var exposeServicesAttr = ReflectionHelper.GetSingleAttributeOrDefault(controllerModel.ControllerType); + if (exposeServicesAttr.IncludeSelf) + { + var exposedControllerModels = application.Controllers + .Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType)) + .ToArray(); + + controllerModelsToRemove.AddRange(exposedControllerModels); + Logger.LogInformation($"Removing the controller{(exposedControllerModels.Length > 1 ? "s" : "")} {exposeServicesAttr.ServiceTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")} from the application model since {(exposedControllerModels.Length > 1 ? "they are" : "it is")} replaced by the controller: {controllerModel.ControllerType.AssemblyQualifiedName}"); + continue; + } + var baseControllerTypes = controllerModel.ControllerType .GetBaseClasses(typeof(Controller), includeObject: false) .Where(t => !t.IsAbstract) .ToArray(); - if (baseControllerTypes.Length > 0) + if (baseControllerTypes.Length == 0) { - derivedControllerModels.Add(controllerModel); - Logger.LogInformation($"Removing the controller {controllerModel.ControllerType.AssemblyQualifiedName} from the application model since it replaces the controller(s): {baseControllerTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")}"); + continue; } + + var baseControllerModels = application.Controllers + .Where(cm => baseControllerTypes.Contains(cm.ControllerType)) + .ToArray(); + + if (baseControllerModels.Length == 0) + { + continue; + } + + controllerModelsToRemove.Add(controllerModel); + Logger.LogInformation($"Removing the controller {controllerModel.ControllerType.AssemblyQualifiedName} from the application model since it replaces the controller(s): {baseControllerTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")}"); } - application.Controllers.RemoveAll(derivedControllerModels); + application.Controllers.RemoveAll(controllerModelsToRemove); } protected virtual void ConfigureRemoteService(ControllerModel controller, [CanBeNull] ConventionalControllerSetting configuration) diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs index 568ecfef7b..a409355ef0 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Hosting; using Volo.Abp; using Volo.Abp.AspNetCore.Auditing; using Volo.Abp.AspNetCore.ExceptionHandling; +using Volo.Abp.AspNetCore.Security; using Volo.Abp.AspNetCore.Security.Claims; using Volo.Abp.AspNetCore.Tracing; using Volo.Abp.AspNetCore.Uow; @@ -82,5 +83,10 @@ namespace Microsoft.AspNetCore.Builder { return app.UseMiddleware(); } + + public static IApplicationBuilder UseAbpSecurityHeaders(this IApplicationBuilder app) + { + return app.UseMiddleware(); + } } } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs new file mode 100644 index 0000000000..28b0f3a48e --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Security +{ + public class AbpSecurityHeadersMiddleware : IMiddleware, ITransientDependency + { + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + /*X-Content-Type-Options header tells the browser to not try and “guess” what a mimetype of a resource might be, and to just take what mimetype the server has returned as fact.*/ + AddHeaderIfNotExists(context, "X-Content-Type-Options", "nosniff"); + + /*X-XSS-Protection is a feature of Internet Explorer, Chrome and Safari that stops pages from loading when they detect reflected cross-site scripting (XSS) attacks*/ + AddHeaderIfNotExists(context, "X-XSS-Protection", "1; mode=block"); + + /*The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a ,