i have a method which takes in a DotNetOpenAuth (formally known as DotNetOpenId) Response object. My method extracts any claimed data, checks to see if this user exists in our system, yadda yadda yadda... and when finished returns the auth'd user instance.
Now .. how can i use moq to mock up this response object, to test my authentication method ( AuthenticateUser() )?
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
User user = null;
try
{
// Extract the claimed information and
// check if this user is valid, etc.
// Any errors with be thrown as Authentication Errors.
user = _authenticationService.AuthenticateUser(response) as User;
}
catch (AuthenticationException exception)
{
ViewData.ModelState.AddModelError("AuthenticationError", exception);
}
.. other code, like forms auth, other response.status' etc. ..
}
Mocking framework: moq
Language: .NET C# 3.5 sp1
Response object: taken from the DotNetOpenAuth framework
I'm not familiar with Moq in particular, but the response object is a type that implements DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationResponse, so it can be easily mocked by creating a class that implements the same interface and is prepared to return the same kinds of values.
...just downloaded Moq and mocked up an IAuthenticationResponse like so:
var response = new Mock<IAuthenticationResponse>(MockBehavior.Loose);
response.SetupGet(r => r.ClaimedIdentifier)
.Returns("http://blog.nerdbank.net/");
response.SetupGet(r => r.Status)
.Returns(AuthenticationStatus.Authenticated);
response.SetupGet(r => r.FriendlyIdentifierForDisplay)
.Returns("blog.nerdbank.net");
IAuthenticationResponse resp = response.Object;
Console.WriteLine(resp.ClaimedIdentifier);
Obviously rather than send the result to Console.WriteLine you would want to pass the resp object to the method you're testing.
Related
I'm trying to set up multitenancy in the application that I'm working on. I added the Global Query Filters and implemented the TenantProvider as suggested here. Note this block of code in the example that I listed:
public interface ITenantProvider
{
Guid GetTenantId();
}
public class DummyTenantProvider : ITenantProvider
{
public Guid GetTenantId()
{
return Guid.Parse("069b57ab-6ec7-479c-b6d4-a61ba3001c86");
}
}
In my case, instead of the DummyTenantProvider, I have implemented the tenant provider that gets tenantId from the HttpContextAccessor.HttpContext.User.GetTenantId(). I understand that the type of HttpContextAccessor.HttpContext.User is of ClaimsPrincipal, so I added the additional method that accepts this ClaimsPrincipal as parameter and returns tenantId:
public static string GetTenantId(this ClaimsPrincipal principal)
{
return principal.FindFirstValue("tenant");
}
Also, I've implemented the Api Authentication with JWT token. In Startup.cs, I added the registration of the authentication like this:
services.AddAuthentication(options =>
{
// some code that is not relevant
}).AddJwtBearer(options =>
{
// some code that is not relevant
options.Events = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
// here I get the needed service and from that service, I get the data that I need for Claims (Roles are needed)
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, JsonConvert.SerializeObject(roles)),
};
var appIdentity = new ClaimsIdentity(claims);
context.Principal.AddIdentity(appIdentity);
},
};
});
Now, the issue that I'm having is when I'm making an HTTP request that's targeting the method in the controller that gets roles for the user (the method is using the same service I'm using in the code above) when debugging the code, first the OnTokenValidated is called and the roles for the user should be populated and added to claims, and then the method in the controller is called. When the OnTokenValidated calls the service and when the request to the database is executed (simple "dbContext.Roles.ToListAsync()" in the repository), the global query filter is applied and tenantId should be added to that database request. But when the global filter is applied, tenantId is null in the GetTenantId method and the code throws ArgumentNullException error. What I can't figure out is why is the tenantId null. My assumption is that when the service is called from OnTokenValidated, that is not the part of the HTTP request, and then the HttpContextAccessor.HttpContext doesn't have the needed value, but I'm not sure if I'm right about that.
I would appreciate some additional insight into what I'm doing wrong here.
If anything else is needed to make things clearer, I'm happy to edit the question.
Thank you all.
For anyone in need of a solution, I was able to resolve this issue by adding the Tenant Id as a new claim in Startup.cs using the TokenValidatedContext (because I have that information in TokenValidatedContext at that moment). Looks something like this:
ctx.HttpContext.User.Identity?.AddClaim(New Claim('tenant'));
Later in the Tenant Provider I have access to this claim.
I'm buildin a console Web API to communicate with a localhost server, hosting computer games and highscores for them. Every time I run my code, I get this charming error:
fail:
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.NotSupportedException: Deserialization of types without a
parameterless constructor, a singular parameterized constructor, or a
parameterized constructor annotated with 'JsonConstructorAttribute' is
not supported. Type 'System.Net.Http.HttpContent'. Path: $ |
LineNumber: 0 | BytePositionInLine: 1.
This is the method I'm using to post to the database. Note that this method is not in the console application. It is in the ASP.NET Core MvC application opening a web browser and listening for HTTP requests (which can come from the console application).
[HttpPost]
public ActionResult CreateHighscore(HttpContent requestContent)
{
string jasonHs = requestContent.ReadAsStringAsync().Result;
HighscoreDto highscoreDto = JsonConvert.DeserializeObject<HighscoreDto>(jasonHs);
var highscore = new Highscore()
{
Player = highscoreDto.Player,
DayAchieved = highscoreDto.DayAchieved,
Score = highscoreDto.Score,
GameId = highscoreDto.GameId
};
context.Highscores.Add(highscore);
context.SaveChanges();
return NoContent();
}
I'm sending POST requests in a pure C# console application, with information gathered from user input, but the result is exactly the same when using Postman for post requests - the above NotSupportedException.
private static void AddHighscore(Highscore highscore)
{
var jasonHighscore = JsonConvert.SerializeObject(highscore);
Uri uri = new Uri($"{httpClient.BaseAddress}highscores");
HttpContent requestContent = new StringContent(jasonHighscore, Encoding.UTF8, "application/json");
var response = httpClient.PostAsync(uri, requestContent);
if (response.IsCompletedSuccessfully)
{
OutputManager.ShowMessageToUser("Highscore Created");
}
else
{
OutputManager.ShowMessageToUser("Something went wrong");
}
}
I'm new to all this HTTP requests stuff, so if you spot some glaring errors in my code, that would be appreciated. Though, the most important question is, what am I missing, and how can I read from the HttpContent object, to be able to create a Highscore object to send to the database?
It seems to be the string jasonHs... line that is the problem, since the app crashed in exactly the same way, when I commented out the rest of the ActionResult method.
Based on your code, we can find that you make a HTTP Post request with a json string data (serialized from a Highscore object) from your console client to Web API backend.
And in your action method, you create an instance of Highscore manually based on received data, so why not make your action accept a Highscore type parameter, like below. Then the model binding system would help bind data to action parameter(s) automatically.
[HttpPost]
public ActionResult CreateHighscore([FromBody]Highscore highscore)
{
//...
when I call my API Webservice its returns an empty array.
In my Header request, i have only a jwt token for authenticating
In Angular:
getSheets(): Observable<Sheet[]> {
return this.http.get(this.config.apiUrl + '/api/SheetsRelationAPI', this.jwt())
.map(this.extractData)
.do(data => console.log('SheetsData:', data)) // debug
.catch(this.handleError);
In Asp.net MVC 5:
[HostAuthentication("bearer")]
[System.Web.Http.Authorize]
public class SheetsRelationAPIController : ApiController
{
private GSheetsContext db = new GSheetsContext();
// GET: api/SheetsRelation
[ResponseType(typeof(SheetsRelationView))]
public IQueryable<SheetsRelationView> GetSheetsRelation()
{
var claims = (User.Identity as System.Security.Claims.ClaimsIdentity).Claims;
var username = "";
foreach (var claim in claims)
if (claim.Type.ToString() == "sub")
{
username = claim.Value.ToString();
}
//var tasks = from tsk in db.SheetsRelation.Include(s => s.SheetsContent.id )
//select tsk;
var sheetsRelation = db.SheetsRelationView.Where(jt => jt.Username == username);
return sheetsRelation;
}
}
UPDATE 1:
It seems it's worked in PostMan and I have a JSON in response But in Angular, i haven't any JSON in response.
Three things you may wish to try -
Not related to this issue, but I always decorate my APIs with the specific http method, to ensure there isnt any confusion on my part - [HttpGet] in this case.
Refactor your API class so that it doesnt have direct dependencies on GSheetsContext and User.Identity (make yourself an injectable service to handle that, so that you can mock the behavior.)
Unit test the controller method with mocked dependencies, so that you can be sure that your controller is behaving as it is expected to.
Edit, if that sounds like too much work
Comment out your existing controller logic and put a stub method there that just returns something like return db.SheetsRelationView.Create()
If that works, then you know your issue is not with your API, and is in the logic. Then refer back to steps 2 & 3 above ;)
Asp.Net Web API Odata Controller Action:
public async Task<IHttpActionResult> Post(Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Products.Add(product);
await db.SaveChangesAsync();
return Created(product);
}
Odata client code:
(Odata v4 client code generator v4)
static void AddProduct(Default.Container container, ProductService.Models.Product product)
{
container.AddToProducts(product);
var serviceResponse = container.SaveChanges();
foreach (var operationResponse in serviceResponse)
{
Console.WriteLine("Response: {0}", operationResponse.StatusCode);
}
}
I would like to handle exception in a proper way inside AddProducts() Method while saving the changes.
How can I catch process the ModelState error which is sent from server return BadRequest(ModelState);?
Finally I just want to show the error message to the end uses which was sent from server.
Example:
"Product category is required."
What is the use of ODataException class? Will this help me?
Please help me.
if I understood well, you want to intercept that the ModelState is not valid, and customize the OData error that is shown to the user.
If you just want that the errors of the invalid model show up in the returned payload, you can use:
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
If you want to fully control the exceptions handling and messages shown, I'd suggest several action points for you to accomplish this:
Intercept ModelState is not valid: you can do this with a custom ActionFilterAttribute. In there, you can override the method OnActionExecuting(HttpActionContext actionContext). You can access the ModelState through actionContext.ModelState, check if it is valid, check the fields that have errors, check the nature of these errors and the generated messages for these errors, etc. The ModelState may be not valid for different reasons, like different types than the expected, not meet requirements specified by DataAnnotations, etc. You can check more on Model validation in here. For your case, I guess the Product entity will have a Required data annotation in the Category field.
After checking all errors, you can throw a custom Exception with the error/list of errors with the messages you want. This is necessary to later intercept your custom exception and be able to return your custom message in the error payload.
Intercept your custom exception: create a custom ExceptionFilterAttribute to intercept your thrown exceptions. Overriding the
OnException(HttpActionExecutedContext filterContext) you will have access to the exception, and inspecting it you will be able to build your proper OdataError:
In here you should return the HttpResponseMessage with the BadRequest http status code and the created ODataError as a payload. As an example of very simple code (you can see that it would depend on how you build your custom exception):
public override void OnException(HttpActionExecutedContext filterContext)
{
Exception ex = filterContext.Exception;
HttpRequestMessage currentRequest = filterContext.Request;
if (filterContext.Exception.GetType() == typeof(YourCustomValidationException))
{
var oDataError = new ODataError()
{
ErrorCode = "invalidModel",
Message = "Your model is not valid.",
InnerError = new ODataInnerError()
{
TypeName = ex.TheEntityThatHasErrors
},
};
foreach (var validationError in ex.ValidationErrors)
{
oDataError.InnerError.Message += validationError + ", ";
}
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.RequestMessage = currentRequest;
response.Content = new StringContent(JsonConvert.SerializeObject(oDataError));
filterContext.Response = response;
}
}
Finally, you will have to setup the custom ActionFilterAttribute and the custom ErrorFilterAttribute to be used each time that a request reach your controller. You can decorate your actions, controllers, or you can set the filters for all your API controllers in the WebApiConfig, with config.Filters.Add(...);
You can find more information about all of this in here. In the end, the error and exception handling is the same for ASP.Net Web API, with or without OData; difference is that if you have an OData API, you should return errors in OData style.
Hope all this info is understandable and helps you somehow.
I'm working on a ASP.NET web application which contains both MVC and WEB API. Can anyone give me an example of how to use ApiController.Unauthorized Method in Web API. I am not sure what kind of parameter I should pass into this method.
If your controller action method return IHttpActionResult then you can use this method as return type.
return Unauthorized();
You can also pass AuthenticationHeaderValue as parameter of this method which represents authentication information in Authorization, ProxyAuthorization, WWW-Authneticate, and Proxy-Authenticate header values.
If your action method does not return IHttpActionResult then you can throw HttpResponseException in anywhere from your controller action.
throw new HttpResponseException(HttpStatusCode.Unauthorized);
If you want to pass a custom message then use
var msg = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{
ReasonPhrase = "Your message!"
};
throw new HttpResponseException(msg);