Mocking ControllerBase.HttpContext.AuthenicateAsync() to return success using SAML2.0 - asp.net-core

I am trying to understand why HttpContext.AuthenticateAsync() method fails when I set the httpContext using the following code
public static void SetMockAuthenticatedControllerContext(this Controller controller,string userName)
{
var httpContext = MockHttpContext(userName);
var controllerContext = new ControllerContext
{
HttpContext= httpContext,
RouteData=new Microsoft.AspNetCore.Routing.RouteData()
};
controller.ControllerContext = controllerContext;
}
public static HttpContext MockHttpContext(string userName)
{
var context = new Mock<HttpContext>();
var request = new Mock<HttpRequest>();
var response = new Mock<HttpResponse>();
var session = new Mock<ISession>();
var user = new Mock<ClaimsPrincipal>();
var identity = new Mock<ClaimsIdentity>();
var features = new Mock<IFeatureCollection>();
var authService = new Mock<IAuthenticationService>();
var prodvierServiceMock = new Mock<IServiceProvider>();
var authTicket = new AuthenticationTicket(new ClaimsPrincipal(), "External");
authService.Setup(c => c.AuthenticateAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
.Returns(Task.FromResult(AuthenticateResult.Success(authTicket)));
prodvierServiceMock.Setup(c => c.GetService(typeof(IAuthenticationService))).Returns(authService);
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
//context.Setup(ctx=>ctx.GetServerVariable(It.IsAny<string>())).Returns()
context.Setup(ctx => ctx.User).Returns(user.Object);
context.Setup(x => x.RequestServices).Returns(prodvierServiceMock.Object);
context.Setup(ctx => ctx.Features).Returns(features.Object);
context.Setup(ctx => ctx.User.Identity).Returns(identity.Object);
identity.Setup(x => x.IsAuthenticated).Returns(true);
identity.Setup(x => x.Name).Returns(userName);
return context.Object;
}
and I am calling this in my controller test class as
follows:
HttpContextFactory.SetMockAuthenticatedControllerContext(mycontrollerInstance,userName);
then
call
var result = controller.Index();
which throws the following error:
Unable to cast object of type 'Moq.Mock`1[Microsoft.AspNetCore.Authentication.IAuthenticationService]' to type 'Microsoft.AspNetCore.Authentication.IAuthenticationService
Please help me understand what i am doing wrong.
Thanks for the help.

Related

Get incremental changes for a group in Microsoft Graph in C#

I have the following code to get users from an AAD group:
public async Task<IGroupTransitiveMembersCollectionWithReferencesPage> GetGroupMembersPageByIdAsync(string groupId)
{
return await graphServiceClient
.Groups[groupId]
.TransitiveMembers
.Request()
.Top(999)
.GetAsync();
}
public async Task<IGroupTransitiveMembersCollectionWithReferencesPage> GetGroupMembersNextPageAsnyc(
IGroupTransitiveMembersCollectionWithReferencesPage groupMembersRef,
string nextPageUrl)
{
groupMembersRef.InitializeNextPageRequest(_graphServiceClient, nextPageUrl);
return await groupMembersRef
.NextPageRequest
.GetAsync();
}
public async Task<(List<AzureADUser> users,
string nextPageUrl,
IGroupTransitiveMembersCollectionWithReferencesPage usersFromGroup)> GetFirstUsersPageAsync(Guid objectId)
{
var users = new List<AzureADUser>();
var usersFromGroup = await GetGroupMembersPageByIdAsync(objectId.ToString());
usersFromGroup.AdditionalData.TryGetValue("#odata.nextLink", out object nextLink1);
var nextPageUrl = (nextLink1 == null) ? string.Empty : nextLink1.ToString();
users.AddRange((IEnumerable<AzureADUser>)(usersFromGroup));
return (users, nextPageUrl, usersFromGroup);
}
public async Task<(List<AzureADUser> users,
string nextPageUrl,
IGroupTransitiveMembersCollectionWithReferencesPage usersFromGroup)> GetNextUsersPageAsync(string nextPageUrl, IGroupTransitiveMembersCollectionWithReferencesPage usersFromGroup)
{
var users = new List<AzureADUser>();
usersFromGroup = await GetGroupMembersNextPageAsnyc(usersFromGroup, nextPageUrl);
usersFromGroup.AdditionalData.TryGetValue("#odata.nextLink", out object nextLink2);
nextPageUrl = (nextLink2 == null) ? string.Empty : nextLink2.ToString();
users.AddRange((IEnumerable<AzureADUser>)(usersFromGroup));
return (users, nextPageUrl, usersFromGroup);
}
I'm trying to learn about how I can use delta query functionality: https://learn.microsoft.com/en-us/graph/delta-query-groups so that next time when I run this, I can get the difference (new users/removed users/updated users) and return that list. Is that possible via delta query functionality?
I had a test in my asp.net core mvc project and you can get delta information by code below.
using Azure.Identity;
using Microsoft.Graph;
public async Task<IActionResult> Index()
{
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_app_id";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
//get group members
var users = await graphClient.Groups["groupId"].TransitiveMembers.Request().Top(999).GetAsync();
//get group member delta info
var delta = await graphClient.Groups.Delta().Request().Filter("id eq 'group_id'").GetAsync();
return View();
}

Couldn't avoid NullReferenceException in Action Filter (ASP.NET Core)

I'm writing an action filter for setting LastAccessDate user property. On retrieving user's record from DB, i'm getting NullReferenceException. How to get rid of this exception? Here is my Action Filter:
public class LogActivity : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var resultContext = await next();
var id = int.Parse(resultContext.RouteData.Values["id"].ToString());
var repo = resultContext.HttpContext.RequestServices.GetService<UserRepo>();
Console.WriteLine(id);
// var user = await repo.GetRespondent(id);
var user= repo.GetRespondent(id).Result; <========= Here Exception occurs
if (user != null)
{
user.LastAccessDate = DateTime.Now;
await repo.SaveAll();
}
}
}
Here is my UserRepo repository's get method:
public async Task<User> GetRespondent(int id)
{
var user= await _context.User.FirstOrDefaultAsync(u => u.Id == id);
if (user!= null)
return user
return null;
}
Replace this line
var user= repo.GetRespondent(id).Result; <========= Here Exception occurs
with
var user = await repo.GetRespondent(id);

Simple serialize ODataQueryOptions

I'm trying to:
[EnableQuery]
[HttpGet]
[ODataRoute("")]
public IHttpActionResult Get(ODataQueryOptions<UserODataModel> options)
{
var users = _repository.RetrieveOData();
var serialQuery = JsonConvert.SerializeObject(options, jsonOptions);
//save serialQuery somewhere
return Ok(users);
}
But got
Newtonsoft.Json.JsonSerializationException: 'Error getting value from 'ReadTimeout' on 'Microsoft.Owin.Host.SystemWeb.CallStreams.InputStream'.'
"Timeouts are not supported on this stream."
I know there is already a question about serialize Stream:
Newtonsoft Json.net - how to serialize content of a stream?
But in this case i can't "extract stream value" from ODataQueryOptions, or can I?
Some ideia?
Since we work on the same company, if anyone is interested, we found a way, maybe not the pretty way, to serialize an ODataQueryOptions:
public static ODataQueryOptions DeserializeQueryOptions(SerializedQueryOptions options)
{
var uri = new Uri(teste.OriginalUri);
var model = ODataConfig.Model; //GetEdmModel
var segment = model.EntityContainer.FindEntitySet(options.EdmType);
var newPath = new Microsoft.AspNet.OData.Routing.ODataPath(new EntitySetSegment(segment));
var httpConfiguration = new HttpConfiguration();
httpConfiguration.EnableDependencyInjection();
var request = new HttpRequestMessage(HttpMethod.Get, uri)
{
Properties =
{
{ HttpPropertyKeys.HttpConfigurationKey, httpConfiguration },
}
};
var context = new ODataQueryContext(model, options.EntityType, newPath);
var oDataQueryOptions = new ODataQueryOptions(context, request);
return oDataQueryOptions;
}
public static SerializedQueryOptions SerializeQueryOptions(ODataQueryOptions options)
{
return new SerializedQueryOptions
{
OriginalUri = options.Request.RequestUri.AbsoluteUri,
EdmType = options.Context.NavigationSource.Name,
EntityType = options.Context.ElementClrType
};
}
After you serialize it to an object you can serialize it to a JSON string:
var queryOptionsSerialized = new SerializedQueryOptions()
{
OriginalUri = "http://localhost:25723/odata/users?$skip=0&$top=2&$orderby=fullName&$count=true",
EdmType = "users",
EntityType = typeof(UserODataModel)
};
var json = JsonConvert.SerializeObject(queryOptionsSerialized);
var deserialized = JsonConvert.DeserializeObject<SerializedQueryOptions>(json);
var options = ODataQueryOptionsHelper.DeserializeQueryOptions(deserialized);
In case One is not using OData routing or using an ApiController (not ODataController),
modify the way of Obtaining ODataPath to:
ODataUriParser parser = new ODataUriParser(model, serviceRoot, requestUri);
ODataPath path = parser.ParsePath();
//var newPath = new Microsoft.AspNet.OData.Routing.ODataPath(new EntitySetSegment(segment));
Microsoft.AspNet.OData.Routing.ODataPath newPath = new Microsoft.AspNet.OData.Routing.ODataPath(path.FirstOrDefault());
where the serviceRoot is the Url part other that the path defined in the model.

ASP.NET CORE using ADO.NET with AutoMapper

What is the proper way of using AutoMapper with ADO.NET in ASP.NET Core in generic way?
Also the SQL query has the same column names as in class of <T>
In specified example variable result is always empty list, so automapper could not map object properties to DbDataReader columns.
public class CustomDbContext : BaseRepository
{
readonly DbConnection dbConn;
public CustomDbContext(RepoDbContext context) : base(context)
{
dbConn = context.Database.GetDbConnection();
}
public async Task<List<T>> Get<T>(string sql) where T : class
{
var config = new AutoMapper.MapperConfiguration(cfg =>
{
cfg.CreateMap<DbDataReader, List<T>>();
});
var mapper = config.CreateMapper();
await dbConn.OpenAsync();
using (var command = dbConn.CreateCommand())
{
command.CommandText = sql;
var reader = await command.ExecuteReaderAsync();
var result = new List<T>();
if (reader.HasRows)
{
await reader.ReadAsync();
result = mapper.Map<DbDataReader, List<T>>(reader);
}
reader.Dispose();
return result;
}
}
}
Should I specify more detailed AutoMapper configuration or it can't be done this way?
Try using interfaces as IDataReader and IEnumerable instead of classes DbDataReader and List.
public async Task<List<T>> Get<T>(string sql) where T : class
{
var config = new AutoMapper.MapperConfiguration(cfg =>
{
cfg.CreateMap<IDataReader, IEnumerable<T>>();
});
var mapper = config.CreateMapper();
await dbConn.OpenAsync();
using (var command = dbConn.CreateCommand())
{
command.CommandText = sql;
var reader = await command.ExecuteReaderAsync();
var result = new List<T>();
if (reader.HasRows)
{
await reader.ReadAsync();
result = mapper.Map<IDataReader, IEnumerable<T>>(reader).ToList();
}
reader.Dispose();
return result;
}
}

how to assign valueprovider to controller from Model object

I have an Edit Post action method in my MVC4 application and I am trying to unit test this action. But, the Unit test fails with "NullReferenceException". Below is the unit test FYR.
[TestMethod]
public void EditAction_Should_Redirect_When_Update_Successful()
{
// Arrange
var mockHttpContext = new Mock<HttpContextBase>();
var mockRequest = new Mock<HttpRequestBase>();
mockHttpContext.Setup(x => x.Request).Returns(mockRequest.Object);
// tell the mock to return "POST" when HttpMethod is called
mockRequest.Setup(x => x.HttpMethod).Returns("POST");
mockRequest.SetupGet(req => req.Form).Returns(new FormCollection());
var controller = GetTheController();
var id = 1;
// assign the fake context
var context = new ControllerContext(mockHttpContext.Object,
new RouteData(),
controller);
controller.ControllerContext = context;
var formValues = new MyModel() {
Id = 1,
ActivityDescription = "This is another description",
CreatedDate", Convert.ToDateTime("31-12-2014"),
UserId = 1,
IsCompleted = false
};
// Act
var result = controller.Edit(id, formValues) as RedirectToRouteResult;
// Assert
Assert.AreEqual("List", result.RouteValues["Action"]);
Assert.AreEqual(id, result.RouteValues["id"]);
}
Edit action method is below -
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(MyModel item)
{
var viewResult = ValidateItem(item);
if (viewResult != null)
return viewResult;
//Unit test is failing at this step.
TryUpdateModel(item);
if (ModelState.IsValid)
{
_itemsRepository.Edit(item);
return RedirectToAction("Index");
}
else return View(item);
}
Below is the stacktrace for reference -
Test Outcome: Failed
Test Duration: 0:00:00.3306816
Result Message:
Test method MvcToDoListItemsDemo.Tests.TodoControllerTest.EditAction_Should_Redirect_When_Update_Successful threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Result StackTrace:
at Microsoft.Web.Infrastructure.DynamicValidationHelper.DynamicValidationShim.IsValidationEnabled(HttpContext context)
at Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.IsValidationEnabled(HttpContext context)
at Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.GetUnvalidatedCollections(HttpContext context, Func`1& formGetter, Func`1& queryStringGetter)
at System.Web.Helpers.Validation.Unvalidated(HttpRequest request)
at System.Web.Mvc.FormValueProviderFactory.<.ctor>b__0(ControllerContext cc)
at System.Web.Mvc.FormValueProviderFactory.GetValueProvider(ControllerContext controllerContext)
at System.Web.Mvc.ValueProviderFactoryCollection.<>c__DisplayClassc.<GetValueProvider>b__7(ValueProviderFactory factory)
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at System.Web.Mvc.ValueProviderFactoryCollection.GetValueProvider(ControllerContext controllerContext)
at System.Web.Mvc.ControllerBase.get_ValueProvider()
at System.Web.Mvc.Controller.TryUpdateModel[TModel](TModel model)
Could someone please advise if I am doing anything wrong here ?
Regards,
Ram
TryUpdateModel(item) gets the updated values for item from the controller's default ValueProvider, usually a System.Web.Mvc.FormValueProvider, which in turn parses them from the current POST request body. In unit tests, you can wrap the model in a DictionaryValueProvider<object> and return it as-is, like this:
var controller = GetTheController();
var requestModel = new MyModel()
{
/* .. values .. */
};
controller.ValueProvider = new DictionaryValueProvider<object>(
new Dictionary<string, object>() { { "MyModel", requestModel } }, null);
var result = controller.Edit(id) as RedirectToRouteResult;