.net web api AuthorizeAttribute pass parameter to the controller - asp.net-web-api

A AuthorizedAttribute named as JwtAuthenticateAttribute is implemented and did something
It can be triggered for each request of the below API
[HttpGet]
[JwtAuthenticate]
public GetFaqListDS Index([FromUri] GetFaqListModel model)
{
List<MP_faqs> res = Faq.GetFaqList(model.curPage, model.pageSize);
var re = Request;
var hds = re.Headers;
GetFaqListDS ds = new GetFaqListDS();
ds.data = res;
return ds;
}
The question is, how to pass a value to this controller's function from the Attribute JwtAuthenticate.
P.S the value exists at the OnAuthorization of this attribute.
Thanks.

Add the required value in "actionContext.Request.Properties" in Onauthorize method and access these in controller.
Adding UserDetail Model :
actionContext.Request.Properties.Add("__user", new UserDetails(){userid=123, username="ABC"});
Retrieving values :
private static UserDetails GetUserDetailsFromRequest()
{
object tempVal;
try
{
var httpRequestMessage = (HttpRequestMessage)HttpContext.Current.Items["MS_HttpRequestMessage"];
httpRequestMessage.Properties.TryGetValue("__user", out tempVal);
if (tempVal == null) return null;
var user = (UserDetails)tempVal;
return user;
}
catch
{
}
return new UserDetails();
}
Hope this will helps you.

Related

Get the api controllers constructor value within an AuthorizeFilter

When the user is authenticated I want to prevent that he updates/deletes/reads data created from other accounts... by telling him you do not have the permission 403!
What is the best way to get an instance of the ISchoolyearService to invoke its HasUserPermission() method?
I know I could new up the SchoolyearService here but that would defeat the reason using an IoContainer at all in my app.
public class UserActionsSchoolyearAuthorizationFilter : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext != null)
{
bool canUserExecuteAction = false;
if (actionContext.Request.Method == HttpMethod.Put)
{
int schoolyearId = Convert.ToInt32(actionContext.Request.GetRouteData().Values["Id"]);
int userId = actionContext.Request.Content.ReadAsAsync<SchoolyearEditRequest>().Result.Schoolyear.UserId;
//var schoolyearService = actionContext.ControllerContext.Controller.GetContstructorParameterServiceInstance();
//canUserExecuteAction = schoolyearService.HasUserPermission(userId, schoolyearId);
if (canUserExecuteAction)
{
base.OnAuthorization(actionContext);
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
}
}
// Removed for brevity
private readonly ISchoolyearService _service;
public SchoolyearController(ISchoolyearService service)
{
_service = service;
}
If you made the _service parameter public on your SchoolyearController you could try something like this in the OnAuthorization method:
var schoolyearController = actionContext.ControllerContext.Controller as SchoolyearController;
canUserExecuteAction = schoolyearController._service.HasUserPermission(userId, schoolyearId);
Ok finally I found it out how to get the ISchoolyearService from the current request:
Grab the registered service from the DependencyScope!
Now this Attribute should be put on the controller directly. Its not needed to put it on the action due to the if/else on the http verbs which I do.
bool canUserExecuteAction = false;
if (actionContext.Request.Method == HttpMethod.Put)
{
int targetId = Convert.ToInt32(actionContext.Request.GetRouteData().Values["Id"]);
int userId = actionContext.Request.Content.ReadAsAsync<SchoolyearEditRequest>().Result.Schoolyear.UserId;
var requstScope = actionContext.ControllerContext.Request.GetDependencyScope();
var service = requstScope.GetService(typeof(ISchoolyearService)) as ISchoolyearService;
canUserExecuteAction = service.HasUserPermission(userId, targetId);
if (canUserExecuteAction)
{
base.OnAuthorization(actionContext);
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
}
}

Unit test WebApi2 passing header values

I am working on a project using WebApi2. With my test project I am using Moq and XUnit.
So far testing an api has been pretty straight forward to do a GET like
[Fact()]
public void GetCustomer()
{
var id = 2;
_customerMock.Setup(c => c.FindSingle(id))
.Returns(FakeCustomers()
.Single(cust => cust.Id == id));
var result = new CustomersController(_customerMock.Object).Get(id);
var negotiatedResult = result as OkContentActionResult<Customer>;
Assert.NotNull(negotiatedResult);
Assert.IsType<OkNegotiatedContentResult<Customer>>(negotiatedResult);
Assert.Equal(negotiatedResult.Content.Id,id);
}
Now I am moving onto something a little complicated where I need to access value from the request header.
I have created my own Ok() result by extending the IHttpActionResult
public OkContentActionResult(T content,HttpRequestMessage request)
{
_request = request;
_content = content;
}
This allows me to have a small helper that reads the header value from the request.
public virtual IHttpActionResult Post(Customer customer)
{
var header = RequestHeader.GetHeaderValue("customerId", this.Request);
if (header != "1234")
How am I meant to setup Moq with a dummy Request?
I have spent the last hour or so hunting for an example that allows me to do this with webapi however I cant seem to find anything.
So far.....and I am pretty sure its wrong for the api but I have
// arrange
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var headers = new NameValueCollection
{
{ "customerId", "111111" }
};
request.Setup(x => x.Headers).Returns(headers);
request.Setup(x => x.HttpMethod).Returns("GET");
request.Setup(x => x.Url).Returns(new Uri("http://foo.com"));
request.Setup(x => x.RawUrl).Returns("/foo");
context.Setup(x => x.Request).Returns(request.Object);
var controller = new Mock<ControllerBase>();
_customerController = new CustomerController()
{
// Request = request,
};
I am not really sure what next I need to do as I havent needed to setup a mock HttpRequestBase in the past.
Can anyone suggest a good article or point me in the right direction?
Thank you!!!
I believe that you should avoid reading the headers in your controller for better separation of concerns (you don't need to read the Customer from request body in the controller right?) and testability.
How I will do it is create a CustomerId class (this is optional. see note below) and CustomerIdParameterBinding
public class CustomerId
{
public string Value { get; set; }
}
public class CustomerIdParameterBinding : HttpParameterBinding
{
public CustomerIdParameterBinding(HttpParameterDescriptor parameter)
: base(parameter)
{
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
actionContext.ActionArguments[Descriptor.ParameterName] = new CustomerId { Value = GetIdOrNull(actionContext) };
return Task.FromResult(0);
}
private string GetIdOrNull(HttpActionContext actionContext)
{
IEnumerable<string> idValues;
if(actionContext.Request.Headers.TryGetValues("customerId", out idValues))
{
return idValues.First();
}
return null;
}
}
Writing up the CustomerIdParameterBinding
config.ParameterBindingRules.Add(p =>
{
return p.ParameterType == typeof(CustomerId) ? new CustomerIdParameterBinding(p) : null;
});
Then in my controller
public void Post(CustomerId id, Customer customer)
Testing the Parameter Binding
public void TestMethod()
{
var parameterName = "TestParam";
var expectedCustomerIdValue = "Yehey!";
//Arrange
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost/someUri");
requestMessage.Headers.Add("customerId", expectedCustomerIdValue );
var httpActionContext = new HttpActionContext
{
ControllerContext = new HttpControllerContext
{
Request = requestMessage
}
};
var stubParameterDescriptor = new Mock<HttpParameterDescriptor>();
stubParameterDescriptor.SetupGet(i => i.ParameterName).Returns(parameterName);
//Act
var customerIdParameterBinding = new CustomerIdParameterBinding(stubParameterDescriptor.Object);
customerIdParameterBinding.ExecuteBindingAsync(null, httpActionContext, (new CancellationTokenSource()).Token).Wait();
//Assert here
//httpActionContext.ActionArguments[parameterName] contains the CustomerId
}
Note: If you don't want to create a CustomerId class, you can annotate your parameter with a custom ParameterBindingAttribute. Like so
public void Post([CustomerId] string customerId, Customer customer)
See here on how to create a ParameterBindingAttribute

Using Url.Link with Attribute Routing in Webapi 2

I want to add a Location header to my http response when using webapi 2. The method below shows how to do this using a named route. Does anyone know if you can create the Url.Link using Attribute Routing feature that was released as part of webapi 2?
string uri = Url.Link("DefaultApi", new { id = reponse.Id });
httpResponse.Headers.Location = new Uri(uri);
Thanks in advance
You can use RouteName with Ur.Link when using attribute routing.
public class BooksController : ApiController
{
[Route("api/books/{id}", Name="GetBookById")]
public BookDto GetBook(int id)
{
// Implementation not shown...
}
[Route("api/books")]
public HttpResponseMessage Post(Book book)
{
// Validate and add book to database (not shown)
var response = Request.CreateResponse(HttpStatusCode.Created);
// Generate a link to the new book and set the Location header in the response.
string uri = Url.Link("GetBookById", new { id = book.BookId });
response.Headers.Location = new Uri(uri);
return response;
}
}
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#route-names
You can do:
[Route("{id}", Name="GetById")]
public IHttpActionResult Get(int id)
{
// Implementation...
}
public IHttpActionResult Post([FromBody] UsuarioViewModel usuarioViewModel)
{
if (!ModelState.IsValid)
return BadRequest();
var link = Url.Link("GetById", new { id = 1});
var content = "a object";
return Created(link, content);
}

How to generate a link to an HTTP POST action with Hyprlinkr?

I'm trying to use Hyprlinkr to generate URL to the HTTP Post action. My controller looks like this:
public class MyController : ApiController {
[HttpPost]
public void DoSomething([FromBody]SomeDto someDto) {
...
}
}
with this route:
routes.MapHttpRoute(
name: "MyRoute",
routeTemplate: "dosomething",
defaults: new { controller = "My", action = "DoSomething" });
I expect to get a simple URL: http://example.com/dosomething, but it does not work. I tried two methods:
1) routeLinker.GetUri(c => c.DoSomething(null)) - throws NullReferenceException
2) routeLinker.GetUri(c => c.DoSomething(new SomeDto())) - generates invalid URL:
http://example.com/dosomething?someDto=Namespace.SomeDto
Update:
Issue opened at github:
https://github.com/ploeh/Hyprlinkr/issues/17
I found a workaround, loosely based on Mark's answer. The idea is to go over every route parameter and remove those that have [FromBody] attribute applied to them. This way dispatcher does not need to be modified for every new controller or action.
public class BodyParametersRemover : IRouteDispatcher {
private readonly IRouteDispatcher _defaultDispatcher;
public BodyParametersRemover(String routeName) {
if (routeName == null) {
throw new ArgumentNullException("routeName");
}
_defaultDispatcher = new DefaultRouteDispatcher(routeName);
}
public Rouple Dispatch(
MethodCallExpression method,
IDictionary<string, object> routeValues) {
var routeKeysToRemove = new HashSet<string>();
foreach (var paramName in routeValues.Keys) {
var parameter = method
.Method
.GetParameters()
.FirstOrDefault(p => p.Name == paramName);
if (parameter != null) {
if (IsFromBodyParameter(parameter)) {
routeKeysToRemove.Add(paramName);
}
}
}
foreach (var routeKeyToRemove in routeKeysToRemove) {
routeValues.Remove(routeKeyToRemove);
}
return _defaultDispatcher.Dispatch(method, routeValues);
}
private Boolean IsFromBodyParameter(ParameterInfo parameter) {
var attributes = parameter.CustomAttributes;
return attributes.Any(
ct => ct.AttributeType == typeof (FromBodyAttribute));
}
}
The second option is the way to go:
routeLinker.GetUri(c => c.DoSomething(new SomeDto()))
However, when using a POST method, you'll need to remove the model part of the generated URL. You can do that with a custom route dispatcher:
public ModelFilterRouteDispatcher : IRouteDispatcher
{
private readonly IRouteDispatcher defaultDispatcher;
public ModelFilterRouteDispatcher()
{
this.defaultDispatcher = new DefaultRouteDispatcher("DefaultApi");
}
public Rouple Dispatch(
MethodCallExpression method,
IDictionary<string, object> routeValues)
{
if (method.Method.ReflectedType == typeof(MyController))
{
var rv = new Dictionary<string, object>(routeValues);
rv.Remove("someDto");
return new Rouple("MyRoute", rv);
}
return this.defaultDispatcher.Dispatch(method, routeValues);
}
}
Now pass that custom dispatcher into your RouteLinker instance.
Caveat: it's very late as I'm writing this and I haven't attempted to compile the above code, but I thought I'd rather throw an attempted answer here than have you wait several more days.
Dimitry's solution got me most of the way to where I wanted, however the routeName ctor param was a problem because StructureMap doesn't know what to put in there. Internally hyprlink is using UrlHelper to generate the URI, and that wants to know the route name to use
At that point, I see why URI generation is so tricky, because it is tied to the route names in the routing config and in order to support POST, we need to associate the method, with the correct routename and that is not known at dispatcher ctor time. Default hyprlinkr assumes there is only one route config named "DefaultRoute"
I changed Dimitry's code as follows, and adopted a convention based approach, where controller methods that start with "Get" are mapped to the route named "Get" and controller methods starting with "Add" are mapped to the route named "Add".
I wonder if there are better ways of associating a method with the proper named routeConfig?
public class RemoveFromBodyParamsRouteDispatcher : IRouteDispatcher
{
private static readonly ILog _log = LogManager.GetLogger(typeof (RemoveFromBodyParamsRouteDispatcher));
public Rouple Dispatch(MethodCallExpression method,
IDictionary<string, object> routeValues)
{
var methodName = method.Method.Name;
DefaultRouteDispatcher defaultDispatcher;
if (methodName.StartsWith("Get"))
defaultDispatcher = new DefaultRouteDispatcher("Get");
else if (methodName.StartsWith("Add"))
defaultDispatcher = new DefaultRouteDispatcher("Add");
else
throw new Exception("Unable to determine correct route name for method with name " + methodName);
_log.Debug("Dispatch methodName=" + methodName);
//make a copy of routeValues as contract says we should not modify
var routeValuesWithoutFromBody = new Dictionary<string, object>(routeValues);
var routeKeysToRemove = new HashSet<string>();
foreach (var paramName in routeValuesWithoutFromBody.Keys)
{
var parameter = method.Method
.GetParameters()
.FirstOrDefault(p => p.Name == paramName);
if (parameter != null)
if (IsFromBodyParameter(parameter))
{
_log.Debug("Dispatch: Removing paramName=" + paramName);
routeKeysToRemove.Add(paramName);
}
}
foreach (var routeKeyToRemove in routeKeysToRemove)
routeValuesWithoutFromBody.Remove(routeKeyToRemove);
return defaultDispatcher.Dispatch(method, routeValuesWithoutFromBody);
}
private static bool IsFromBodyParameter(ParameterInfo parameter)
{
//Apparently the "inherit" argument is ignored: http://msdn.microsoft.com/en-us/library/cwtf69s6(v=vs.100).aspx
const bool msdnSaysThisArgumentIsIgnored = true;
var attributes = parameter.GetCustomAttributes(msdnSaysThisArgumentIsIgnored);
return attributes.Any(ct => ct is FromBodyAttribute);
}
}

Why WebAPI does not use my JSONP formatter to deserialize model?

I'm very very confuse about Microsoft brand-new framework, ASP.NET MVC WebAPI. I try to create complete solution for cross-site API with JSONP data.
First, I modify their default WebApiConfig to the following code.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new {id = RouteParameter.Optional});
// Custom customization
config.Formatters.Clear();
config.Formatters.Add(new JsonpFormatter());
}
}
I use jQuery to create a request to this API website.
// jQuery will create HTTP GET the following URL
// http://localhost:3557/api/FlightAvailability/SearchFlight?callback=jQuery18206342989655677229_1353568617029&origin=JFK&destination=SLC&isOneWayFlight=false&departFlightDate=Wed%2C+28+Nov+2012+17%3A00%3A00+GMT&returnFlightDate=Wed%2C+05+Dec+2012+17%3A00%3A00+GMT&numberOfGuests=1&numberOfChildren=1&numberOfInfants=1&preferredCurrency=USD&query=%7B+Origin%3A+'JFK'+%7D&flightDate=Wed%2C+28+Nov+2012+17%3A00%3A00+GMT&_=1353568618465
$.ajax
({
url: 'http://localhost:3557/api/FlightAvailability/SearchFlight',
dataType: 'jsonp',
data: $.postify(model),
success: processResponse
});
I create action to handle above request. Everything is correct. I can call to this action but WebAPI doesn't use my JSONP formatter to deserialize my query object.
However, I try to directly call ContentNegotiator to get which formatter that handle my request. It's quite surprise that negotiatorResult is my JSONP formatter.
[HttpGet]
public List<FlightInfo> SearchFlight(FlightAvailabilityQuery query)
{
var negotiator = Configuration.Services.GetContentNegotiator();
var negotiatorResult = negotiator.Negotiate(typeof (FlightAvailabilityQuery), Request, Configuration.Formatters);
var flight = new FlightsAvailability();
var result = flight.GetAvailability(WebApiAuthentication.UserInfo.SessionService, query);
return result;
}
Why WebAPI does not use my JSONP formatter to deserialize query FlightAvailabilityQuery object?
PS. I try to break all possible line in JSONP formatter but Visual Studio doesn't hit any break point by it directly go to action method without call at my only one formatter. However, when I directly call ContentNegotiator, it hit at my break point correctly.
Update #1 - Add JSONP formatter source code
public class JsonpFormatter : JsonMediaTypeFormatter
{
private readonly JsonSerializerSettings _serializerSettings;
private string _jsonpCallbackFunction;
public JsonpFormatter()
{
JsonpParameterName = "callback";
_serializerSettings = new JsonSerializerSettings();
_serializerSettings.TypeNameHandling = TypeNameHandling.Objects;
_serializerSettings.Converters.Add(new IsoDateTimeConverter());
MediaTypeMappings.Add(new ExtendedQueryStringMapping(JsonpParameterName, "application/json"));
}
public string JsonpParameterName { get; set; }
public override bool CanReadType(Type type)
{
return true;
}
public override bool CanWriteType(Type type)
{
return true;
}
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
var formatter = new JsonpFormatter()
{
_jsonpCallbackFunction = GetJsonCallbackFunction(request)
};
// this doesn't work unfortunately
//formatter.SerializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
formatter.SerializerSettings.Converters.Add(new StringEnumConverter());
formatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
formatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
return formatter;
}
public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)
{
// Create a serializer
var serializer = JsonSerializer.Create(_serializerSettings);
// Create task reading the content
return Task.Factory.StartNew(() =>
{
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
{
using (var jsonTextReader = new JsonTextReader(streamReader))
{
return serializer.Deserialize(jsonTextReader, type);
}
}
});
}
public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)
{
if (string.IsNullOrEmpty(_jsonpCallbackFunction))
return base.WriteToStreamAsync(type, value, stream, content, transportContext);
StreamWriter writer = null;
// write the pre-amble
try
{
writer = new StreamWriter(stream);
writer.Write(_jsonpCallbackFunction + "(");
writer.Flush();
}
catch (Exception ex)
{
try
{
if (writer != null)
writer.Dispose();
}
catch { }
var tcs = new TaskCompletionSource<object>();
tcs.SetException(ex);
return tcs.Task;
}
return base.WriteToStreamAsync(type, value, stream, content, transportContext)
.ContinueWith(innerTask =>
{
if (innerTask.Status == TaskStatus.RanToCompletion)
{
writer.Write(")");
writer.Flush();
}
}, TaskContinuationOptions.ExecuteSynchronously)
.ContinueWith(innerTask =>
{
writer.Dispose();
return innerTask;
}, TaskContinuationOptions.ExecuteSynchronously)
.Unwrap();
}
private string GetJsonCallbackFunction(HttpRequestMessage request)
{
if (request.Method != HttpMethod.Get)
return null;
var query = HttpUtility.ParseQueryString(request.RequestUri.Query);
var queryVal = query[this.JsonpParameterName];
if (string.IsNullOrEmpty(queryVal))
return null;
return queryVal;
}
}
Your action does not get hit because it cannot model bind your query parameter. Also JsonP is for HTTP GET only, so your formatter will not be selected for deserialization. How do you expect your FlightAvailabilityQuery being deserialized? I saw a lot of query parameters from your URL, do you want that be turned into FlightAvailabilityQuery?
The easiest way to get that is to use FromUri.
public List<FlightInfo> SearchFlight([FromUri]FlightAvailabilityQuery query)
If for some reason that does not work, you can try to add individual query parameter name on the action, such as origin, isOneWay, destination. etc. Then inside your action construct the FlightAvailabilityQuery object.
Also, if you have a lot of actions that you want to reuse this model binding logic, you can register a custom parameter binding to solve that. Please see this link for how to register a custom parameter binding to solve this.
Hope this helps!

Resources