From d9aba97213469dac9bdbd16f41d082a160bf51f0 Mon Sep 17 00:00:00 2001 From: Chebotov Nick Date: Tue, 7 May 2019 23:31:58 +0300 Subject: [PATCH] Add extension-method "HidePathItemsWithoutAcceptedRoles" for hiding all SwaggerDocument PathItems with added Security information for OAuth2 without accepted roles; 2) Change extension-method name from "EnumsWithValuesFixFilters" to "AddEnumsWithValuesFixFilters"; 3) Update "README". --- CHANGELOG.md | 6 + README.md | 139 ++++++++++++++++-- appveyor.yml | 2 +- assets/hideSwaggerDocumentPathItems.png | Bin 0 -> 16685 bytes .../Extensions/SwaggerDocumentExtensions.cs | 84 +++++++++++ .../Extensions/SwaggerGenOptionsExtensions.cs | 2 +- ...e.Swashbuckle.AspNetCore.Extensions.csproj | 3 +- .../Controllers/SamplePersonController.cs | 6 +- test/WebApi2.0-Swashbuckle/Startup.cs | 35 ++++- 9 files changed, 254 insertions(+), 23 deletions(-) create mode 100644 assets/hideSwaggerDocumentPathItems.png create mode 100644 src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerDocumentExtensions.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cc75ee..5cc7c1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ These are the changes to each version that has been released on the [nuget](https://www.nuget.org/packages/Unchase.Swashbuckle.AspNetCore.Extensions/). +## v1.1.0 `(2019-05-07)` + +- [x] Add extension-method `HidePathItemsWithoutAcceptedRoles` for hiding all SwaggerDocument PathItems with added Security information for OAuth2 without accepted roles +- [x] Change extension-method name from `EnumsWithValuesFixFilters` to `AddEnumsWithValuesFixFilters` +- [x] Update [`README`](https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/) + ## v1.0.0 `(2019-05-01)` - [x] Add an output enums integer values with there strings like `0 = FirstEnumValue` without a `StringEnumConverter` in swaggerUI and schema (by default enums will output only their integer values) diff --git a/README.md b/README.md index e0c7705..8958691 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,19 @@ ## Getting Started -To use the extensions: - +To use the extensions: 1. First install the [NuGet package](https://www.nuget.org/packages/Unchase.Swashbuckle.AspNetCore.Extensions/): ```powershell Install-Package Unchase.Swashbuckle.AspNetCore.Extensions ``` -2. In the _ConfigureServices_ method of _Startup.cs_, inside your `AddSwaggerGen` call, enable whichever extensions (filters) you need: +2. Then use whichever extensions (filters) you need + +## Extensions (Filters) use + +1. **For fix enums**: +- In the _ConfigureServices_ method of _Startup.cs_, inside your `AddSwaggerGen` call, enable whichever extensions (filters) you need: ```csharp // This method gets called by the runtime. Use this method to add services to the container. @@ -24,14 +28,18 @@ public void ConfigureServices(IServiceCollection services) // Add framework services. services.AddMvc(); - services.AddSwaggerGen(c => + services.AddSwaggerGen(options => { - c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" }); + options.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" }); options.SwaggerDoc("v1", new Info {Title = "My API", Version = "v2"}); // Add filters to fix enums - options.EnumsWithValuesFixFilters(true); + options.AddEnumsWithValuesFixFilters(true); + // or custom use: + //options.SchemaFilter(); // add schema filter to fix enums (add 'x-enumNames' for NSwag) in schema + //options.ParameterFilter(); // add parameter filter to fix enums (add 'x-enumNames' for NSwag) in schema parameters + //options.DocumentFilter(true); // add document filter to fix enums displaying in swagger document // Include xml-comments from xml-file generated by project var filePath = Path.Combine(AppContext.BaseDirectory, "WebApi2.0-Swashbuckle.xml"); @@ -40,6 +48,55 @@ public void ConfigureServices(IServiceCollection services) } ``` +2. **For hiding SwaggerDocumentation PathItems** without accepted roles: +- In the _ConfigureServices_ method of _Startup.cs_, inside your `AddSwaggerGen` call, enable whichever extensions (filters) you need: + +```csharp +// This method gets called by the runtime. Use this method to add services to the container. +public void ConfigureServices(IServiceCollection services) +{ + ... + + services.AddSwaggerGen(options => + { + ... + + // add Security information to each operation for OAuth2 + options.OperationFilter(); + + // optional: if you're using the SecurityRequirementsOperationFilter, you also need to tell Swashbuckle you're using OAuth2 + options.AddSecurityDefinition("oauth2", new ApiKeyScheme + { + Description = "Standard Authorization header using the Bearer scheme. Example: \"bearer {token}\"", + In = "header", + Name = "Authorization", + Type = "apiKey" + }); + }); +} +``` + +- In the _Configure_ method of _Startup.cs_, inside your `UseSwagger` call, enable whichever extensions (filters) you need: + +```csharp +// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. +public void Configure(IApplicationBuilder app, IHostingEnvironment env) +{ + ... + + app.UseSwagger(c => + { + c.PreSerializeFilters.Add((swaggerDoc, httpRequest) => + { + // hide all SwaggerDocument PathItems with added Security information for OAuth2 without accepted roles (for example, "AcceptedRole") + swaggerDoc.HidePathItemsWithoutAcceptedRoles(new List {"AcceptedRole"}); + }); + }); + + ... +} +``` + ## Builds status |Status|Value| @@ -55,8 +112,10 @@ public void ConfigureServices(IServiceCollection services) ## Features +#### Fix enums + - Add an output enums integer values with there strings like `0 = FirstEnumValue` without a `StringEnumConverter` in swaggerUI and schema (by default enums will output only their integer values) -- Add description to each enum value that has an `[Description]` attribute in `swaggerUI` and schema +- Add description to each enum value that has an `[Description]` attribute in `swaggerUI` and schema - should use *options.DocumentFilter\(**true**);* or *options.AddEnumsWithValuesFixFilters(**true**);* In [`Swashbuckle SwaggerUI`](https://github.com/domaindrivendev/Swashbuckle): @@ -100,6 +159,35 @@ public void ConfigureServices(IServiceCollection services) [EnumMember] Mr } + + ... + + /// + /// Sample Person request and response. + /// + public class SamplePersonRequestResponse + { + /// + /// Sample Person title. + /// + public Title Title { get; set; } + + /// + /// Sample Person age. + /// + public int Age { get; set; } + + /// + /// Sample Person firstname. + /// + [Description("The first name of the person")] + public string FirstName { get; set; } + + /// + /// Sample Person income. + /// + public decimal? Income { get; set; } + } ``` - Fix enum values in generated by [`NSwagStudio`](https://github.com/RicoSuter/NSwag/wiki/NSwagStudio) or [Unchase OpenAPI Connected Service](https://marketplace.visualstudio.com/items?itemName=Unchase.unchaseopenapiconnectedservice) client classes: @@ -118,16 +206,35 @@ public void ConfigureServices(IServiceCollection services) Mr = 2, } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "9.13.35.0 (Newtonsoft.Json v11.0.0.0)")] - public enum SamplePersonRequestResponseTitle + ``` + +#### Hide PathItems + +- Hide all SwaggerDocument PathItems with added Security information for OAuth2 (with [Swashbuckle.AspNetCore.Filters](https://github.com/mattfrear/Swashbuckle.AspNetCore.Filters)) without accepted roles: + + ![Hide SwaggerDocument PathItems](assets/hideSwaggerDocumentPathItems.png) + + You should use `AuthorizeAttribute` for methods or controllers: + + ```csharp + ... + public class SamplePersonController : ControllerBase { - None = 0, - - Miss = 1, - - Mr = 2, - + // this method will not be hidden with using 'swaggerDoc.HidePathItemsWithoutAcceptedRoles(new List {"AcceptedRole"});' + [Authorize(Roles = "AcceptedRole")] + [HttpGet] + public ActionResult Get(Title title) + { + ... + } + + // this method will be hidden with using 'swaggerDoc.HidePathItemsWithoutAcceptedRoles(new List {"AcceptedRole"});' + [Authorize(Roles = "NotAcceptedRole")] + [HttpPost] + public ActionResult Post([FromBody] SamplePersonRequestResponse request) + { + ... + } } ``` diff --git a/appveyor.yml b/appveyor.yml index 87fabcf..2f5e5db 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.0.{build} +version: 1.1.{build} pull_requests: do_not_increment_build_number: true skip_tags: true diff --git a/assets/hideSwaggerDocumentPathItems.png b/assets/hideSwaggerDocumentPathItems.png new file mode 100644 index 0000000000000000000000000000000000000000..52f07cdfc888ce5a1ce735c1465180a9e6ab1e3c GIT binary patch literal 16685 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 k`B?wazW@jmHr+e acceptedRoles) + { + var acceptedRolesList = acceptedRoles.ToList(); + foreach (var swaggerDocPath in swaggerDoc.Paths.Values) + { + if (!HasNecessaryRoles(swaggerDocPath.Get, acceptedRolesList)) + swaggerDocPath.Get = null; + + if (!HasNecessaryRoles(swaggerDocPath.Post, acceptedRolesList)) + swaggerDocPath.Post = null; + + if (!HasNecessaryRoles(swaggerDocPath.Patch, acceptedRolesList)) + swaggerDocPath.Patch = null; + + if (!HasNecessaryRoles(swaggerDocPath.Delete, acceptedRolesList)) + swaggerDocPath.Delete = null; + + if (!HasNecessaryRoles(swaggerDocPath.Put, acceptedRolesList)) + swaggerDocPath.Put = null; + } + } + + internal static bool HasNecessaryRoles(Operation swaggerDocPathOperation, List acceptedRoles) + { + var founded = false; + if (swaggerDocPathOperation?.Security == null) + return true; + + if (!acceptedRoles.Any()) + return true; + + foreach (var operationSecurity in swaggerDocPathOperation.Security) + { + if (founded) + break; + + if (operationSecurity != null) + { + foreach (var operationSecurityValue in operationSecurity.Values) + { + if (founded) + break; + + var operationSecurityValueRuntimeFields = operationSecurityValue?.GetType().GetRuntimeFields(); + if (operationSecurityValueRuntimeFields != null) + { + foreach (var operationSecurityValueRuntimeField in operationSecurityValueRuntimeFields) + { + if (founded) + break; + + if (operationSecurityValueRuntimeField?.GetValue(operationSecurityValue) is List authorizeAttributes) + { + foreach (var authorizeAttribute in authorizeAttributes) + { + var authorizeAttributeRoles = authorizeAttribute?.Roles?.Split(',').Select(r => r.Trim()).ToList(); + var intersect = authorizeAttributeRoles?.Intersect(acceptedRoles); + if (intersect == null || !intersect.Any()) + { + founded = true; + break; + } + } + } + } + } + } + } + } + + return !founded; + } + } +} diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs index 1d5ea09..f955950 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/SwaggerGenOptionsExtensions.cs @@ -8,7 +8,7 @@ namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions { public static class SwaggerGenOptionsExtensions { - public static void EnumsWithValuesFixFilters(this SwaggerGenOptions swaggerGenOptions, bool includeDescriptionFromAttribute = false) + public static void AddEnumsWithValuesFixFilters(this SwaggerGenOptions swaggerGenOptions, bool includeDescriptionFromAttribute = false) { swaggerGenOptions.SchemaFilter(); swaggerGenOptions.ParameterFilter(); diff --git a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj index eb88898..3e69682 100644 --- a/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj +++ b/src/Unchase.Swashbuckle.AspNetCore.Extensions/Unchase.Swashbuckle.AspNetCore.Extensions.csproj @@ -14,10 +14,11 @@ 7.3 https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/blob/master/assets/icon.png?raw=true - 1.0.1 + 1.1.0 + diff --git a/test/WebApi2.0-Swashbuckle/Controllers/SamplePersonController.cs b/test/WebApi2.0-Swashbuckle/Controllers/SamplePersonController.cs index a0c7aa8..2858ce9 100644 --- a/test/WebApi2.0-Swashbuckle/Controllers/SamplePersonController.cs +++ b/test/WebApi2.0-Swashbuckle/Controllers/SamplePersonController.cs @@ -1,4 +1,6 @@ -using Microsoft.AspNetCore.Mvc; +using System.Net; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using WebApi2._0_Swashbuckle.Models; namespace WebApi2._0_Swashbuckle.Controllers @@ -18,6 +20,7 @@ public class SamplePersonController : ControllerBase /// Sample Person title. /// Returns sample Person response. /// Returns sample Person response. + [Authorize(Roles = "AcceptedRole")] [HttpGet("get")] [Consumes("application/json")] [ProducesResponseType(typeof(SamplePersonRequestResponse), 200)] @@ -40,6 +43,7 @@ public ActionResult Get(Title title) /// Sample Person request. /// Returns sample Person response. /// Returns sample Person response. + [Authorize(Roles = "NotAcceptedRole")] [HttpPost("post")] [Consumes("application/json")] [ProducesResponseType(typeof(SamplePersonRequestResponse), 200)] diff --git a/test/WebApi2.0-Swashbuckle/Startup.cs b/test/WebApi2.0-Swashbuckle/Startup.cs index 2c6be7b..58ad633 100644 --- a/test/WebApi2.0-Swashbuckle/Startup.cs +++ b/test/WebApi2.0-Swashbuckle/Startup.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -6,8 +7,10 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; +using Swashbuckle.AspNetCore.Filters; using Swashbuckle.AspNetCore.Swagger; using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions; +using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; namespace WebApi2._0_Swashbuckle { @@ -24,18 +27,37 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + //services.AddAuthentication(); + services.AddMvc() .AddJsonOptions(options => options.SerializerSettings.Formatting = Formatting.Indented) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddSwaggerGen(options => { - options.SwaggerDoc("v1", new Info {Title = "My API", Version = "v2"}); + options.SwaggerDoc("v1", new Info {Title = "My API", Version = "v1"}); + + options.AddEnumsWithValuesFixFilters(true); - options.EnumsWithValuesFixFilters(true); + // or custom use: + //options.SchemaFilter(); // add schema filter to fix enums (add 'x-enumNames') in schema + //options.ParameterFilter(); // add parameter filter to fix enums (add 'x-enumNames') in schema parameters + //options.DocumentFilter(true); // add document filter to fix enums displaying in swagger document var filePath = Path.Combine(AppContext.BaseDirectory, "WebApi2.0-Swashbuckle.xml"); options.IncludeXmlComments(filePath); + + // add Security information to each operation for OAuth2 + options.OperationFilter(); + + // if you're using the SecurityRequirementsOperationFilter, you also need to tell Swashbuckle you're using OAuth2 + options.AddSecurityDefinition("oauth2", new ApiKeyScheme + { + Description = "Standard Authorization header using the Bearer scheme. Example: \"bearer {token}\"", + In = "header", + Name = "Authorization", + Type = "apiKey" + }); }); } @@ -49,7 +71,14 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) app.UseMvc(); - app.UseSwagger(); + app.UseSwagger(c => + { + c.PreSerializeFilters.Add((swaggerDoc, httpRequest) => + { + // hide all SwaggerDocument PathItems with added Security information for OAuth2 without accepted roles (for example, "AcceptedRole") + swaggerDoc.HidePathItemsWithoutAcceptedRoles(new List {"AcceptedRole"}); + }); + }); app.UseSwaggerUI(c => {