Skip to content

Commit

Permalink
Add extension-method "HidePathItemsWithoutAcceptedRoles" for hiding a…
Browse files Browse the repository at this point in the history
…ll SwaggerDocument PathItems with added Security information for OAuth2 without accepted roles;

2) Change extension-method name from "EnumsWithValuesFixFilters" to "AddEnumsWithValuesFixFilters";
3) Update "README".
  • Loading branch information
unchase committed May 7, 2019
1 parent a13f31b commit d9aba97
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 23 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
139 changes: 123 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<XEnumNamesSchemaFilter>(); // add schema filter to fix enums (add 'x-enumNames' for NSwag) in schema
//options.ParameterFilter<XEnumNamesParameterFilter>(); // add parameter filter to fix enums (add 'x-enumNames' for NSwag) in schema parameters
//options.DocumentFilter<DisplayEnumsWithValuesDocumentFilter>(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");
Expand All @@ -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<SecurityRequirementsOperationFilter>();

// 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<string> {"AcceptedRole"});
});
});

...
}
```

## Builds status

|Status|Value|
Expand All @@ -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\<DisplayEnumsWithValuesDocumentFilter\>(**true**);* or *options.AddEnumsWithValuesFixFilters(**true**);*

In [`Swashbuckle SwaggerUI`](https://github.com/domaindrivendev/Swashbuckle):

Expand Down Expand Up @@ -100,6 +159,35 @@ public void ConfigureServices(IServiceCollection services)
[EnumMember]
Mr
}

...

/// <summary>
/// Sample Person request and response.
/// </summary>
public class SamplePersonRequestResponse
{
/// <summary>
/// Sample Person title.
/// </summary>
public Title Title { get; set; }

/// <summary>
/// Sample Person age.
/// </summary>
public int Age { get; set; }

/// <summary>
/// Sample Person firstname.
/// </summary>
[Description("The first name of the person")]
public string FirstName { get; set; }

/// <summary>
/// Sample Person income.
/// </summary>
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:
Expand All @@ -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<string> {"AcceptedRole"});'
[Authorize(Roles = "AcceptedRole")]
[HttpGet]
public ActionResult<SamplePersonRequestResponse> Get(Title title)
{
...
}

// this method will be hidden with using 'swaggerDoc.HidePathItemsWithoutAcceptedRoles(new List<string> {"AcceptedRole"});'
[Authorize(Roles = "NotAcceptedRole")]
[HttpPost]
public ActionResult<SamplePersonRequestResponse> Post([FromBody] SamplePersonRequestResponse request)
{
...
}
}
```

Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 1.0.{build}
version: 1.1.{build}
pull_requests:
do_not_increment_build_number: true
skip_tags: true
Expand Down
Binary file added assets/hideSwaggerDocumentPathItems.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Swashbuckle.AspNetCore.Swagger;

namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions
{
public static class SwaggerDocumentExtensions
{
public static void HidePathItemsWithoutAcceptedRoles(this SwaggerDocument swaggerDoc, IEnumerable<string> 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<string> 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<AuthorizeAttribute> 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<XEnumNamesSchemaFilter>();
swaggerGenOptions.ParameterFilter<XEnumNamesParameterFilter>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
<NeutralLanguage></NeutralLanguage>
<LangVersion>7.3</LangVersion>
<PackageIconUrl>https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions/blob/master/assets/icon.png?raw=true</PackageIconUrl>
<Version>1.0.1</Version>
<Version>1.1.0</Version>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="4.5.5" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="4.0.1" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -18,6 +20,7 @@ public class SamplePersonController : ControllerBase
/// <param name="title">Sample Person title.</param>
/// <returns>Returns sample Person response.</returns>
/// <response code="200">Returns sample Person response.</response>
[Authorize(Roles = "AcceptedRole")]
[HttpGet("get")]
[Consumes("application/json")]
[ProducesResponseType(typeof(SamplePersonRequestResponse), 200)]
Expand All @@ -40,6 +43,7 @@ public ActionResult<SamplePersonRequestResponse> Get(Title title)
/// <param name="request">Sample Person request.</param>
/// <returns>Returns sample Person response.</returns>
/// <response code="200">Returns sample Person response.</response>
[Authorize(Roles = "NotAcceptedRole")]
[HttpPost("post")]
[Consumes("application/json")]
[ProducesResponseType(typeof(SamplePersonRequestResponse), 200)]
Expand Down
Loading

0 comments on commit d9aba97

Please sign in to comment.