Best way to hide a parameter in Swagger - c#

I'm building an API where I have a couple of endpoints where I need the User ID, so after getting the idea from the most voted answer (not the accepted one) from this post: ASP.NET MVC Pass object from Custom Action Filter to Action.
I created an Action Filter that I can set up just with the [UserIdFromToken] attribute.
So I'm basically getting the User ID through this Action Filter that gets it from the authorization header this way:
[Authorize]
[UserIdFromToken]
public IActionResult Get(Guid userId /* Got from the [UserIdFromToken] filter */)
{
return Ok(_userService.Get(userId));
}
The only problem is now it's showing the parameter userId as a Required parameter (and I don't want to show it at all) in my Swagger documentation, which is obviously false, since I don't need any input beyond the authorization header.
Is there any way to get my swagger documentation cleaner, without showing this parameter?
Do you think it would be better to try another approach to get the userId such as the accepted option from the post I sent above?

I realize this is an old post, but if anyone have similar issues you can create your own action filter by extending Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter like this:
public class HideUserIdOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var scopes = context.MethodInfo
.GetCustomAttributes(true)
.OfType<UserIdFromTokenAttribute>()
.Distinct();
if (scopes.Any())
{
operation.Parameters = operation.Parameters.Where(param => param.Name != "userId").ToList();
}
}
}
Remember to add the operation filter to your swagger gen:
c.OperationFilter<HideUserIdOperationFilter>();

Related

ASP.NET Core 2.2 MVC : wrap responses

I have an application developed in ASP.NET Core MVC with a set of controllers for normal view responses and Web API.
I am trying to figure a correct way to wrap all Web API responses with a consistent class.
My first question is what would be a correct approach to wrap the responses coming from Web API controllers. Since I have two controller types, I would need to distinguish between them as the responses should only be wrapped for API controller, and not view controllers.
As I understand there are two choices a middleware or an action filter.
At first I thought the middleware would be the best choice, but then I realized that I would still need to figure out what kind of request is being processed which would add more potential headache with maintenance?
Then I looked at an action filter and it seems that it would be a better choice to plugin the wrapping handling.
For example an action filter can be added to a base controller just for Web API and not controllers handling the views.
So the question is whether the action filters are best approach to achieve this?
I would recommend you to look at result filters for this. Result filters run after a controller action has produced a result, and it allows you to look at that result and then perform some action.
For example, when you return View in a controller action, then what gets returned is a ViewResult which makes it easy to identify results that would cause a Razor view to be rendered.
Within an API controller, you would usually return a ActionResult<T>, some model object directly, or an ObjectResult. The former two will be automatically converted into an ObjectResult as the action gets executed. So you can just look for ObjectResults and then change the result to have some wrapper object instead. This would look something like this:
public class WrapApiResponseResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
var result = context.Result;
if (result is ObjectResult)
{
// wrap the inner object
var newValue = new WrapperObject(result.Value);
// replace the result
context.Result = new ObjectResult(newValue)
{
// copy the status code
StatusCode = result.StatusCode,
};
}
}
public void OnResultExecuted(ResultExecutedContext context)
{ }
}

Injecting route values based on the current Principal

I've got a web api that accepts authentication through either an api key, a user access token, or a client access token. I've already written a DelegatingHandler for each case, that creates a new ClaimsPrincipal based on the given authentication details, and have confirmed that the principal is accessible within a controller action.
What I want to do now is inject either a company, a user, or a publisher into the route values, so I can create overloads on the controller for each case. What class/interface do I need to extend in order to plug into the pipeline with the following conditions:
Has access to the current principal
Has access to the route data
The action has not yet been selected
Edit
I'm not looking to sidestep routing here - I still want MVC to choose a route for me, based on the route values. I just want to add one more parameter to the route values before it chooses, and the parameter will have a different name and type depending on whether a user access token is used, or an api key is used.
I believe the subject of authentication should result in two distinct methods, because an api key can access all resources for a company, while a user access token can only access the resources they have been given permission to view.
I do not see a reason why you would want to go with a controller here. You would be sidestepping routing, a very opinionated piece of MVC. I would create middleware that runs before MVC (which is, itself, just middleware) instead.
If you're looking to affect RouteData inline, I would look into using a global IResourceFilter or IAsyncResourceFilter. Then, you can update the RouteData property on the ResourceExecutingContext based upon the conditions you specified in your question.
Any additional dependencies you need to determine how to populate the RouteData property can be injected into the resource filter's constructor as specified in the section on dependency injection.
public class SetRouteValueResourceFilter : IAsyncResourceFilter {
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) {
var company = context.HttpContext.Identity.FindFirst("Company")?.Value;
if (company != null) {
context.RouteData.Values.Add("company", company);
}
await next();
}
}
This answer is just an idea. Even though this is handled before the action logic, I'm not sure it will affect which route is selected. According to this diagram, routes are selected before filters are ran.
I managed to get this going by making a custom ValueProviderFactory, which reads values from the current principal's claims and makes them available for parameter binding:
public class ClaimsPrincipalValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
if (actionContext.RequestContext.Principal != null && actionContext.RequestContext.Principal is ClaimsPrincipal principal)
{
var pairs = principal.Claims.Select(claim => new KeyValuePair<string, string>(claim.Type, claim.Value));
return new NameValuePairsValueProvider(pairs, CultureInfo.CurrentCulture);
}
return null;
}
}
In order to use it, you can annotate the input parameters with the ValueProvider attribute:
public class FooController : ApiController
{
[HttpGet]
public void Bar([ValueProvider(typeof(ClaimsPrincipalValueProviderFactory))]ApiKey apiKey)
{
// ...
}
}
That's pretty ugly and unreadable, what I really wanted was something like the FromUri attribute, but for claims. ValueProviderAttribute and FromUriAttribute both inherit from ModelBinderAttribute, so I created a class which does the same:
/// <summary>
/// The action parameter comes from the user's claims, if the user is authorized.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class FromClaimsAttribute : ModelBinderAttribute
{
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
return parameter.BindWithModelBinding(new ClaimsPrincipalValueProviderFactory());
}
}
Now the example method on the FooController looks a lot more readable:
[HttpGet]
public void Bar([FromClaims]ApiKey apiKey)
{
// ...
}
Update
Looks like this is still having problems with route selection and overloading, especially when some of the parameters are nullable and the value is null. I'm going to have to keep looking into this.
Update #2
I managed to simplify the value provider stuff a lot, after finding there is a built-in NameValuePairValueProvider

Best practice: User Identites as parameters?

I am currently developing an API with ASP.NET in C#.
One endpoint needs to call another in order to return a value.
public class Testcontroller : BaseApiController
{
[Authorize]
[HttpGet]
[Route("1", Name = "F1")]
public async Task<IHttpActionResult> F1()
{
return await F2(); // calls 2nd method
}
[Authorize]
[HttpGet]
[Route("2", Name = "F2")]
public async Task<IHttpActionResult> F2()
{
int I = 2;
return Ok(I.ToString());
}
}
Ok, the returned value of this whole thing will be 2, which is absolutely fine. However, in the real API, the second methods needs to retrieve some data about the user. That is normally handled with
var Name = ClaimsPrincipal.Current.Identity.Name;
var CurrentUser = await this.AppUserManager.FindByNameAsync(Name);
These two lines get the user information through the bearer token, that is passed to the method through the authorization process.
Considering this, the first function could call the other one. The downside is that those two LOCs for the user data do not work because this token is not passed properly.
How would you suggest working around this problem? I thought about adding an optional parameter and passing the CurrentUser through that. But I thought that might cause some issues in terms of security?
Thanks for your help!
Firstly don't use ClaimsPrincipal.Current. In Controllers you have a User property, that's where the identity is. ClaimsPrincipal.Current is a hang over from .NET 3.5. If you are going to move to .NET Core, then the User property is the right way to do it.
In response to your actual question there's no security issues in passing that through to other functions outside your controller.

Claims athorization service API usability

Is the following API of a claims authorization service ok, from the point of view of usability?
/* before UPDATE it was like this:
var canEdit = Authz.ForNewRequest()
.WithActionName("edit")
.WithResourceName("project")
.WithResourceClaim("DepartmentId","21")
.CheckAccess(); */
//UPDATE:
var deptId = SomehowGetDepartmentIdAtRuntime(targetProject);
var canEdit = Authz.ForNewRequest()
.WithActionName("edit")
.WithResourceName("project")
.WithResourceClaim("DepartmentId",deptId)
.CheckAccess();
if (canEdit)
{
//edit project
}
And configuration like this:
var authorizationModel = Authorization.ConfigNamespace.AuthzConfig
.LoadAuthorizationModelFromXml("authz.xml");
Authorization.ConfigNamespace.AuthzConfig
.SetApplicationAuthorization(authorizationModel);
Or custom configuration like this:
var authzCustomConfig = Authorization.ConfigNamespace.AuthzConfig
.NewCustomConfiguration()
.WithCustomClaimBasedFactFunctions(claimBasedFunctions)
.WithCustomClaimProviders(claimProviders)
.WithCustomCompositeFactFunctions(compositeFactFunctions)
.WithCustomObligations(obligations);
var authorizationModel = Authorization.ConfigNamespace.AuthzConfig
.LoadAuthorizationModelFromXml("authz.xml", authzCustomConfig);
Authorization.ConfigNamespace.AuthzConfig
.SetApplicationAuthorization(authorizationModel);
Basically, the question is about the top part of the iceberg, i.e. how to use the service, but not how to implement or design the inner part. But just in case, here are a couple of general words about this service:
This service gives an answer of true/false for the given authorization request.
Authorization request has information about:
Action (action name, for example)
Resource (resource name, resource properties)
Subject (user name, user id, user roles, user properties)
Due to the fact that i use Microsoft.IdentityModel:
The properties of an action, resource, or a subject are presented as Claims. Approximately, a claim is an object, which has a value, value name, and value type. For example, a claim for a user could have the following info: ("UserName", "Andrey", "string").
The authorization request is the AuthorizationContext class from the Microsoft.IdentityModel.Claims namespace.
Two more things to consider:
I'd like this service to be a general solution, which could fit not only to one certain project.
Logic could be involved in order to understand if a condition for the permission decision is met or not.
That's why custom logic might be injected. Those claimBasedFunctions, claimProviders, compositeFactFunctions, and obligations are the custom logic. But they don't really matter for the question, just some custom confuguration elements, implementations of the interfaces, which are defined in the authorization assembly. The question is not about what they should be, or how they should work. You can think of them as of any interface implementatons that have to be injected.
Thanks!
P.S. this question is off-topic for the Code Review site.
If I interpret your description correctly, the following code means: "If you want to edit a project, you need a claim with name DepartmentId that has a value of 21.
var canEdit = Authz.ForNewRequest()
.WithActionName("edit")
.WithResourceName("project")
.WithResourceClaim("DepartmentId","21")
.CheckAccess();
if (canEdit)
{
//edit project
}
This statement would be at the start of your Edit action in your Project controller in case you were building an MVC application.
If my interpretation is correct, I would advise you to remove the WithActionName and WithResourceName methods. Those things can be retrieved from the context in which this code is executing. Your fluent API is too easy to copy from one method to another and forget to update those strings. I would look at a custom authorize attribute that you attach to an action in which you checks the claims.
UPDATE:
I was thinking something like this:
public class ProjectController : ApiController
{
[HttpPost]
[MyAuthorize("DepartmentId","21")
public void Edit(string applicationName)
{
// business logic
}
}
Inside the MyAuthorize attribute implementation you can retrieve the controller- and action names. If the developer using the attribute doesn't have to specify this, he/she can't get it wrong.

asp web api patch implementation

Assume i have this model
public partial class Todo
{
public int id { get; set; }
public string content { get; set; }
public bool done { get; set; }
}
And i send this as json data to my controller as a patch request.
This is mearly the action of toggeling a checkbox.
I think it makes sence that i only want to sent that to my server, and not the entire model.
{ "id":1, "done" : true }
What does my WebApi controller need to look like in order to correctly process this, simple, json patch request ? Should i be using web api for this, or should i use a more rpc styled approach with mvc ?
It seems like a very basic thing to do, but i can't seem to get it right !
I think i might need to use a different parameter in my controller method, but i'm not sure.
Thank you for your time.
You can find PATCH feature in the OData pre-release Nuget package: Microsoft.AspNet.WebApi.OData.
Information how you can use it to create an action for handling PATCH can be found in the Partial Updates (PATCH requests) section of the blog post about OData support in ASP.NET Web API.
Changing the method to PATCH doesn't change Web API behaviour in any way. There is no built in mechanism for doing partial updates. One of the reasons there was no PATCH method for so long is that there is no ubiquitous media type for applying patches to resources.
Secondly, you are asking Web API to do object serialization for you so there just is no such concept of applying a partially updated object. There would be so many conventions to agree on, what does a null value mean, what about an empty value, how do I say "don't update this DateTime". What about related objects, child items? How do you cause a child item to be deleted? Unless the CLR team implements some concept of a type that only contains a subset of members from another type, partial updates and object serialization are not going to go well together.
Aliostad mentions UpdateModel and that is possible when updating from a HTML form because the media type application/x-www-form-urlencoded explicitly allows for an arbitrary set of name value pairs. There is no "object serialization" going on. It is just a match of names from the form being matched to names on the Model object.
For myself, I created a new media type I use to do partial updates that works like a form but is more advanced in that it can handle hierarchial data and it maintains an order to the updates.
ASP.NET Web API seems to be missing UpdateModel, TryUpdateModel, etc.
In ASP.NET MVC, you could use them to achieve the desired effect. I have created a work item in ASP.NET Web Stack which you can vote for and if it gets enough votes, it will be implemented.
I used Microsoft.AspNet.WebApi.OData for my project and I had some problems working with JSON (working with numbers in my case). Also, the OData package has some dependencies which, from my point of view, are too big for a single feature (~7MB with all dependecies).
So I developed a simple library which do what you are asking for: SimplePatch.
How to use
Install the package using:
Install-Package SimplePatch
Then in your controller:
[HttpPatch]
public IHttpActionResult PatchOne(Delta<Todo> todo)
{
if (todo.TryGetPropertyValue(nameof(Todo.id), out int id)) {
// Entity to update (from your datasource)
var todoToPatch = Todos.FirstOrDefault(x => x.id == id);
if (todoToPatch == null) return BadRequest("Todo not found");
todo.Patch(todoToPatch);
// Now todoToPatch is updated with new values
} else {
return BadRequest();
}
return Ok();
}
The library support massive patch too:
[HttpPatch]
public IHttpActionResult PatchMultiple(DeltaCollection<Todo> todos)
{
foreach (var todo in todos)
{
if (todo.TryGetPropertyValue(nameof(Todo.id), out int id))
{
// Entity to update (from your datasource)
var entityToPatch = Todos.FirstOrDefault(x => x.id == Convert.ToInt32(id));
if (entityToPatch == null) return BadRequest("Todo not found (Id = " + id + ")");
person.Patch(entityToPatch);
}
else
{
return BadRequest("Id property not found for a todo");
}
}
return Ok();
}
If you use Entity Framework, you have to add only two lines of code after the call to the Patch method:
entity.Patch(entityToPatch);
dbContext.Entry(entityToPatch).State = EntityState.Modified;
dbContext.SaveChanges();
Furthermore, you can exclude some properties to be updated when the Patch method is called.
Global.asax or Startup.cs
DeltaConfig.Init((cfg) =>
{
cfg.ExcludeProperties<Todo>(x => x.id);
});
This is usefull when you are working with an entity and you don't want to create a model.

Categories