1

I am using Swagger with ASP.NET Web API application. If I visit URL http://localhost:5000/swagger

Swagger list all the controllers and actions defined in these controllers. Lets say I have five controllers and each controller has one action. I want to create multiple views such that when

user says http://localhost:5000/swagger/v1 he gets to see all controllers when user says http://localhost:5000/swagger/v2 he gets to see only one controller when user says http://localhost:5000/swagger/v3 he gets to see only two controller

Basically I am trying to restrict access to controller via swagger. Based on user requirement, I will share specific URL with them.

Is it possible to achieve this with Swagger?

SharpCoder
  • 18,279
  • 43
  • 153
  • 249

1 Answers1

3

Yes, you can do exactly what you want.

You should do the following steps:

  1. Create a class that inherits from IDocumentFilter and register it in SwaggerConfig.cs as follows c.DocumentFilter<HideSwaggerEndpointsDocumentFilter>();

Example:

public class HideSwaggerEndpointsDocumentFilter : IDocumentFilter
{
    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
    //enter code here
    }
}

This filter is loaded once you load the swagger page. Inside it, you have control over each and every controller action. You can delete some actions based on any criteria decided by you.

  1. Deleting them is a bit tricky, I do it as follows:
foreach (var apiDescription in apiExplorer.ApiDescriptions)
{
    var route = "/" + apiDescription.RelativePath.Substring(0, (apiDescription.RelativePath.IndexOf('?') != -1) ? apiDescription.RelativePath.IndexOf('?') : apiDescription.RelativePath.Length).TrimEnd('/');
    var path = swaggerDoc.paths[route];

    switch (apiDescription.HttpMethod.Method)
    {
        case "DELETE": path.delete = null; break;
        case "GET": path.get = null; break;
        case "HEAD": path.head = null; break;
        case "OPTIONS": path.options = null; break;
        case "PATCH": path.patch = null; break;
        case "POST": path.post = null; break;
        case "PUT": path.put = null; break;
        default: throw new ArgumentOutOfRangeException("Method name not mapped to operation");
    }

    if (path.delete == null && path.get == null &&
        path.head == null && path.options == null &&
        path.patch == null && path.post == null && path.put == null)
    {
        swaggerDoc.paths.Remove(route);
    }
}

Disclaimer:
If you put the above code in your DocumentFilter class it will delete all actions regardless of the given URL.

  1. So we are in the final step, where you basically do your desired logic.

Inside the (foreach (var apiDescription in apiExplorer.ApiDescriptions)) you can play and do your custom logic. You have access to HttpContext.Current, so you can get the current URL.

If you don't want to delete the current action have something like this, before the swaggerDoc.paths.Remove(route);.

bool forDelete = false; // your custom logic when it should be deleted
if (!forDelete)
{
    return;
}

Hope this helps you.

G.Dimov
  • 2,173
  • 3
  • 15
  • 40
  • 1
    +1. This helps but it wont create a separate version of swagger API. Basically I want to create two URL. One will have all endpoints. Second one will have selected endpoints. else with this one, I need check logged-in user and then delete certain endpoints. – SharpCoder Apr 10 '20 at 08:08
  • The way I have done this -> I have one Login View (requires password and email) and after successful login, you are given a token. In one of the claims of the token I have information what Controllers (or if you want to go further actions) can the current user see. After the login the user is redirected to Swagger page and the token is sent as a cookie. So in my `HideSwaggerEndpointsDocumentFilter` I have access to the token and which controllers the user can and can't see. If someone goes directly to Swagger, he has no cookie, so no controllers are shown. – G.Dimov Apr 10 '20 at 08:19
  • This swagger based implementation, of course, is only made for testing the API and does not restrict someone with a token to call a method that he does not see in Swagger (via Postman for example). So, for that reason, I have created one `ActionFilterAttribute` with its method `OnActionExecuting` where I do basically the same check. Whether the method has `Authorize` attribute and if it does, if he has access with the given token. – G.Dimov Apr 10 '20 at 08:22
  • This is not exactly how you want it done. I play with Cookies and tokens, not with the URL. My guess your way is also doable. – G.Dimov Apr 10 '20 at 08:23