I have just added token-based security to my Web API using ASP.net identity OWIN and OAuth 2. As a result of this I am getting 405 unauthorized error on all my tests. How can I mock the securitycontext. I've seen some samples where other have overridden the Thread.CurrentPrincipal but unsure if this is the correct way.
sample test
[TestMethod]
public void Verify_GetReferenceData_Http_Get()
{
var configAE = new HttpSelfHostConfiguration("http://localhost:53224");
Konstrukt.SL.AggregationEngine.WebApiConfig.Register(configAE, new AutoFacStandardModule());
using (HttpSelfHostServer serverAE = new HttpSelfHostServer(configAE))
{
serverAE.OpenAsync().Wait();
HttpResponseMessage responseMessage;
using (var client = new HttpClient())
{
responseMessage =
client.GetAsync(
"http://localhost:53224/AggregationEngine/GetReferenceData/1/Dummy/..."
).Result;
serverAE.CloseAsync().Wait();
configAE.Dispose();
Assert.AreEqual(HttpStatusCode.OK, responseMessage.StatusCode, "Wrong http status returned");
}
}
}
sample controller
public class GetReferenceDataController : ApiController
{
private readonly IDeserializeHelper _deserializeHelper;
private readonly IGetBudgetData _getBudgetData;
private readonly IRevision _revision;
public GetReferenceDataController(IDeserializeHelper deserializeHelper, IGetBudgetData getBudgetData, IRevision revision)
{
_deserializeHelper = deserializeHelper;
_getBudgetData = getBudgetData;
_revision = revision;
}
[Authorize]
[Route("AggregationEngine/GetReferenceData/{budgetId}/{userId}/{filterJSON}")]
[HttpGet]
public HttpResponseMessage Get(int budgetId, string userId, [FromUri]string filterJSON)
{
FlatBudgetData data = new FlatBudgetData();
IDataQueryFilter dataQueryFilter = _deserializeHelper.DeserializeToFilterObject(EntityType.UserReferenceLine, _revision.GetLatesRevision(budgetId), userId, filterJSON);
data.Data = _getBudgetData.GetData(dataQueryFilter);
string jsonFlatBudget = JsonConvert.SerializeObject(data);
var jsonResponse = new HttpResponseMessage()
{
Content = new StringContent(jsonFlatBudget)
};
jsonResponse.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return jsonResponse;
}
}
I followed the first answer in the following stack thread and got it working. Integration Test Web Api With [Authorize]
Related
I consumed a get api by creating a base service like that:
public class BaseService<T> where T : class
{
private string baseUrl = "http://192.168.43.137:45455/api";
public IEnumerable<T> GetRequest(string uri)
{
baseUrl += uri;
var client = new RestClient(baseUrl);
var request = new RestRequest(Method.GET);
var response = client.Execute(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string json = response.Content;
return JsonConvert.DeserializeObject<IEnumerable<T>>(json);
}
return default;
}
and a userService like this:
public class UserServices {
public IEnumerable<User> GetUsers()
{
var requestService = new BaseService<User>();
return requestService.GetRequest("/Users");
}
It worked fine for the get but I don't know how to consume the post api like this
You can check the microsoft document "Consume a RESTful web service":
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/data-cloud/web-services/rest
It shows you how to deal with the different kinds of requests, and there is a sample link in the bottom which you can refer to.
I require an access token to test methods in a service. I tried to hardcode the access token and it is working well. If I run the application after some time all test cases get failed. I believe it's because the access token gets expired. How can I generate access tokens without hardcoding?
The following is the way I used in my work.
First portion inject configuration and second is for getting access token. Hope so it will help you.
public class TestConfigurationManager
{
private IConfiguration _config;
public TestConfigurationManager()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IConfiguration>(Configuration);
}
public IConfiguration Configuration
{
get
{
if (_config == null)
{
var builder = new ConfigurationBuilder().AddJsonFile($"testsettings.json", optional: false);
_config = builder.Build();
}
return _config;
}
}
}
[Fact(DisplayName = "GetAccessToken")]
public async Task GetAccessToken()
{
var _configuration = new TestConfigurationManager().Configuration;
var _httpMessageHandler = new Mock<HttpMessageHandler>();
var httpClient = new HttpClient(_httpMessageHandler.Object);
TokenRequest tokenRequest = new TokenRequest();
_configuration.GetSection(AppSetting.Accounting.Token).Bind(tokenRequest);
string baseAddress = $"{_configuration[AppSetting.Accounting.TokenURL]}"; ;
var form = new Dictionary<string, string>
{
{"grant_type", tokenRequest.GrantType},
{"username", tokenRequest.UserName},
{"password", tokenRequest.Password},
{"key", tokenRequest.Key}
};
httpClient = new HttpClient
{
BaseAddress = new System.Uri(_configuration[AppSetting.Accounting.BaseUrl])
};
HttpResponseMessage tokenResponse = await httpClient.PostAsync(baseAddress, new FormUrlEncodedContent(form));
var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
var tok = JsonConvert.DeserializeObject<TokenResponse>(jsonContent);
Assert.NotNull(tok?.AccessToken);
}
I have an Asp.net web api with one method that receive a crypted string with variable length, my problem is it string some times could take a length near 5000.
When that method receive a string under 1930 length it process everything ok, but for lengths over 1929 returns
404 Operation returned an invalid status code 'BadRequest'
I changed method GET to POST but problem persists.
I changed filter options on server to very very long limits, but problem persists
This is my controller:
public class LicencingManagerController : ApiController
{
[HttpPost]
[Route(nameof(SaveCustomer2))]
public string SaveCustomer2(string custInfo)
{
return $"ItsOk = {custInfo}";
}
}
Actually I made calls to controller using autogenerated c# client by AutoRest 2.0.483.
I tried hand building PostAsync Request using HttpClient but nothing works.
string custInfo = "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";
var pairs = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("custInfo", custInfo)
};
var content = new FormUrlEncodedContent(pairs);
var client = new HttpClient();
// call sync
var response = client.PostAsync($"{Modelos.Shared.HttpsServicesReference.Licencing}/LicencingAPI/SaveCustomer2", content).Result;
if (response.IsSuccessStatusCode)
{
}
What is the right way to process this controller call?
There is another approach to perform this ?
Create model to hold data
public class Customer {
public string CustumerInfo { get; set; }
}
Update API to expect model in body of request
public class LicencingManagerController : ApiController {
[HttpPost]
[Route(nameof(SaveCustomer2))]
public IHttpActionResult SaveCustomer2([FromBody]Customer model) {
if(!ModelState.IsValid)
return BadRequest(ModelState);
var custInfo = model.CustumerInfo;
return Ok($"ItsOk = {custInfo}");
}
}
Post content to API in body of the request like you did before
string custInfo = "H4sIAAAAAAAEAA...";
string url = $"{Modelos.Shared.HttpsServicesReference.Licencing}/LicencingAPI/SaveCustomer2";
var pairs = new List<KeyValuePair<string, string>> {
new KeyValuePair<string, string>("CustumerInfo", custInfo)
};
var content = new FormUrlEncodedContent(pairs);
var client = new HttpClient();
var response = await client.PostAsync(url, content);
if (response.IsSuccessStatusCode) {
}
When starting up a fresh .Net Core 2.0 project with Azure AD Authentication you get a working sample that can sign in to your tenant, great!
Now I want to get an access token for the signed in user and use that to work with Microsoft Graph API.
I am not finding any documentation on how to achieve this. I just want a simple way to get an access token and access the graph API, using the template created when you start a new .NET Core 2.0 project. From there I should be able to figure out the rest.
Very important that it works with the project that gets created when following the process where you select Work and school accounts for authentication when creating a new 2.0 MVC Core app in Visual Studio.
I wrote a blog article which shows just how to do that: ASP.NET Core 2.0 Azure AD Authentication
The TL;DR is that you should add a handler like this for when you receive an authorization code from AAD:
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("Authentication").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async ctx =>
{
var request = ctx.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(ctx.Options.ClientId, ctx.Options.ClientSecret);
var distributedCache = ctx.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
string userId = ctx.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(distributedCache, userId);
var authContext = new AuthenticationContext(ctx.Options.Authority, cache);
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
ctx.ProtocolMessage.Code, new Uri(currentUri), credential, ctx.Options.Resource);
ctx.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
};
});
Here my context.Options.Resource is https://graph.microsoft.com (Microsoft Graph), which I'm binding from config along with other settings (client id etc.).
We redeem a token using ADAL, and store the resulting token in a token cache.
The token cache is something you will have to make, here is the example from the example app:
public class AdalDistributedTokenCache : TokenCache
{
private readonly IDistributedCache _cache;
private readonly string _userId;
public AdalDistributedTokenCache(IDistributedCache cache, string userId)
{
_cache = cache;
_userId = userId;
BeforeAccess = BeforeAccessNotification;
AfterAccess = AfterAccessNotification;
}
private string GetCacheKey()
{
return $"{_userId}_TokenCache";
}
private void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
Deserialize(_cache.Get(GetCacheKey()));
}
private void AfterAccessNotification(TokenCacheNotificationArgs args)
{
if (HasStateChanged)
{
_cache.Set(GetCacheKey(), Serialize(), new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1)
});
HasStateChanged = false;
}
}
}
The token cache here uses a distributed cache to store tokens, so that all instances serving your app have access to the tokens. They are cached per user, so you can retrieve a token for any user later.
Then when you want to get a token and use MS graph, you'd do something like (important stuff in GetAccessTokenAsync()):
[Authorize]
public class HomeController : Controller
{
private static readonly HttpClient Client = new HttpClient();
private readonly IDistributedCache _cache;
private readonly IConfiguration _config;
public HomeController(IDistributedCache cache, IConfiguration config)
{
_cache = cache;
_config = config;
}
[AllowAnonymous]
public IActionResult Index()
{
return View();
}
public async Task<IActionResult> MsGraph()
{
HttpResponseMessage res = await QueryGraphAsync("/me");
ViewBag.GraphResponse = await res.Content.ReadAsStringAsync();
return View();
}
private async Task<HttpResponseMessage> QueryGraphAsync(string relativeUrl)
{
var req = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0" + relativeUrl);
string accessToken = await GetAccessTokenAsync();
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return await Client.SendAsync(req);
}
private async Task<string> GetAccessTokenAsync()
{
string authority = _config["Authentication:Authority"];
string userId = User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(_cache, userId);
var authContext = new AuthenticationContext(authority, cache);
string clientId = _config["Authentication:ClientId"];
string clientSecret = _config["Authentication:ClientSecret"];
var credential = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenSilentAsync("https://graph.microsoft.com", credential, new UserIdentifier(userId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
}
There we acquire a token silently (using the token cache), and attach it to requests to the Graph.
I noticed in one of the ASP.NET Web API getting started tutorials that the option to add a test project is disabled and I'm not sure why that would be. Would I just test an ASP.NET Web API project like any other MVC project?
I'm prototyping something and I'm being lazy and just using an MVC project and returning JSON from some controllers to mock up a web service. As things start getting a bit more serious, I need to start doing things "more correctly".
So how should I write tests for an ASP.NET Web API project and, more broadly, how could I automate the testing of the actual web service?
I have done this like so:
[TestFixture]
public class CountriesApiTests
{
private const string BaseEndPoint = "http://x/api/countries";
[Test]
public void Test_CountryApiController_ReturnsListOfEntities_ForGet()
{
var repoMock = new Mock<ISimpleRepo<Country>>();
ObjectFactory.Initialize(x => x.For<ISimpleRepo<Country>>().Use(repoMock.Object));
repoMock.Setup(x => x.GetAll()).Returns(new List<Country>
{
new Country {Name = "UK"},
new Country {Name = "US"}
}.AsQueryable);
var client = new TestClient(BaseEndPoint);
var countries = client.Get<IEnumerable<CountryModel>>();
Assert.That(countries.Count(), Is.EqualTo(2));
}
}
TestClient code:
public class TestClient
{
protected readonly HttpClient _httpClient;
protected readonly string _endpoint;
public HttpStatusCode LastStatusCode { get; set; }
public TestClient(string endpoint)
{
_endpoint = endpoint;
var config = new HttpConfiguration();
config.ServiceResolver.SetResolver(new WebApiDependencyResolver());
config.Routes.MapHttpRoute("default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
_httpClient = new HttpClient(new HttpServer(config));
}
public T Get<T>() where T : class
{
var response = _httpClient.GetAsync(_endpoint).Result;
response.EnsureSuccessStatusCode(); // need this to throw exception to unit test
return response.Content.ReadAsAsync<T>().Result;
}
}