I am new to TDD and RhinoMocks.
I am trying to test AssertWasCalled but having problems. The constructor to my test is as follows:
public AccountControllerTests()
{
_webAuthenticator = MockRepository.GenerateMock<IWebAuthenticator>();
}
And my test is like this:
[TestMethod]
public void AccountControllerCallsWebAuthenticator_CreateSignInTicketForGoodLoginCredentials()
{
const string username = "good-username";
const string password = "good-password";
var model = new LoginModel { Username = username, Password = password };
_webAuthenticator.Stub(w => w.Authenticate(username, password)).Return(true);
var mockHttpContextBase = MockRepository.GenerateMock<HttpContextBase>();
var accountController = new AccountController(_webAuthenticator);
accountController.Login(model);
_webAuthenticator.AssertWasCalled(x => x.CreateSignInTicket(mockHttpContextBase, username));
}
The error I get is:
Test method Paxium.Music.WebUI.Tests.Controllers.AccountControllerTests.AccountControllerCallsWebAuthenticator_CreateSignInTicketForGoodLoginCredentials threw exception:
Rhino.Mocks.Exceptions.ExpectationViolationException: IWebAuthenticator.CreateSignInTicket(Castle.Proxies.HttpContextBaseProxy7f274f09b6124e6da32d96dc6d3fface, "good-username"); Expected #1, Actual #0.
I have now changed my code as below - Before and after code:
Before:
public class AccountController : Controller
{
private readonly IWebAuthenticator _webAuthenticator;
public AccountController(IWebAuthenticator webAuthenticator)
{
_webAuthenticator = webAuthenticator;
}
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
if (_webAuthenticator.Authenticate(model.Username, model.Password))
{
_webAuthenticator.CreateSignInTicket(HttpContext, model.Username);
return RedirectToAction("Index", "Home");
}
return View(model);
}
return View(model);
}
}
After:
public class AccountController : Controller
{
private readonly IWebAuthenticator _webAuthenticator;
private readonly HttpContextBase _contextBase;
public AccountController()
{
}
public AccountController(IWebAuthenticator webAuthenticator, HttpContextBase contextBase)
{
_webAuthenticator = webAuthenticator;
_contextBase = contextBase;
}
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
if (_webAuthenticator.Authenticate(model.Username, model.Password))
{
_webAuthenticator.CreateSignInTicket(_contextBase, model.Username);
return RedirectToAction("Index", "Home");
}
return View(model);
}
return View(model);
}
}
My tests now pass. How I inject in the contextBase though when my controller is used for real?? I am using StructureMap.
The error message you are receiving indicates that the Assert failed, i.e. the webAuthenticator object was not called with those specific arguments (hence expected #1, actual #0 exception message).
From the limited context you provide, I suspect that the fake instance of the HttpContextBase (mockHttpContextBase) in your test is not the same object that's being passed to the webAuthenticator from your production code.
There's two ways you can go about this: make the assert less strict or make sure the production code uses the fake http context object. If you don't care which instance of HttpContext gets passed to the webAuthenticator in this test, you can use argument matchers (Rhinomocks calls them argument constraints).
In your case, this would turn out something like this:
_webAuthenticator.AssertWasCalled(x => x.CreateSignInTicket(Arg<HttpContextBase>.Is.Anything, Arg<string>.Is.Equal(username)));
Related
I got a middleware in my application that reads a JWT token and adds to HttpContext.Items["User"] that user model.
I can read this during my request by reading it from the HttpContext.
What I was trying to do Is a model binder that automatically does that for me so the code looks cleaner.
Right now I have it working like the following:
[HttpPost]
[JwtAuthorize]
[Route("readerfrombinder")]
public async Task<IActionResult> ComesFromContext()
{
var user = Request.HttpContext.Items["User"] as TokenUser;
return Ok("You Have Access with binded user "+user.Id);
}
But what im trying to achieve is something like
[HttpPost]
[JwtAuthorize]
[Route("readerfrombinder")]
public async Task<IActionResult> ComesFromBind([ModelBinder] TokenUser comesFromBind)
{
return Ok("You Have Access with binded user "+comesFromBind.Id);
}
I wrote a binder to try this out:
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Login.Jwt;
public class UserBinder : IModelBinder
{
private readonly TokenUser _context;
public UserBinder(TokenUser context)
{
_context = context;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var model = bindingContext.HttpContext.Items["User"] as TokenUser;
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
And added this binder to my user object:
[ModelBinder(BinderType = typeof(UserBinder))]
public class TokenUser
{
public string Id { get; set; }
public string SessionTicket { get; set; }
}
But apparently I'm missing something.
Would appreciate any help ! Thanks !
Security Configuration doesn't let me use antMatchers() on some pages. Below is a configuration code where I'm trying to let not signed in user access "/", "/entries", "/signup". With "/signup" there is no problem it let me visit that page, but it keeps redirecting me to login page if I'm trying to access "/" or "/entries". I've tried to write each uri in separate antMatchers() and switching orders, but no luck so far.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DetailService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(User.PASSWORD_ENCODER);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/entries","/signup").permitAll()
.antMatchers("/adminpanel/**")
.access("hasRole('ROLE_ADMIN')")
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(loginSuccessHandler())
.failureHandler(loginFailureHandler())
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/clearConnection")
.and()
.csrf();
http.headers().frameOptions().disable();
}
public AuthenticationSuccessHandler loginSuccessHandler() {
return (request, response, authentication) -> response.sendRedirect("/");
}
public AuthenticationFailureHandler loginFailureHandler() {
return (request, response, exception) -> {
response.sendRedirect("/login");
};
}
#Bean
public EvaluationContextExtension securityExtension() {
return new EvaluationContextExtensionSupport() {
#Override
public String getExtensionId() {
return "security";
}
#Override
public Object getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication) {
};
}
};
}
}
Apparently I had a UserHandler class that has annotation #ControllerAdvice(basePackages = "myproject.web.controller"). That's means that it applies to all classes for provided package. My addUser() is trying to add User as an attribute and if there is no user it throwing one of exceptions defined in the same class which cause redirection. So, I created separate GuestController outside of the package provided for #ControllerAdvice and handle all logic for a guest in it. That solved my problem. Would appreciate any insights on my approach, if its good practice or not.
#ControllerAdvice(basePackages = "myproject.web.controller")
public class UserHandler {
#Autowired
private UserService users;
#ExceptionHandler(AccessDeniedException.class)
public String redirectNonUser(RedirectAttributes attributes) {
attributes.addAttribute("errorMessage", "Please login before accessing website");
return "redirect:/login";
}
#ExceptionHandler(UsernameNotFoundException.class)
public String redirectNotFound(RedirectAttributes attributes) {
attributes.addAttribute("errorMessage", "Username not found");
return "redirect:/login";
}
#ModelAttribute("currentUser")
public User addUser() {
if(SecurityContextHolder.getContext().getAuthentication() != null) {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = users.findByUsername(username);
if(user != null) {
return user;
} else {
throw new UsernameNotFoundException("Username not found");
}
} else {
throw new AccessDeniedException("Not logged in");
}
}
}
I'm starting a new project using MVC 5, Identity 2.x, Unity, and Dapper. I'm using the standard EF functionality for Identity but using Dapper for the rest of the DB access. I'm using a Repository Pattern for all my (non-Identity) DB calls.
I'm fairly new to Unity and Dapper but keep gettin a "Object reference not set to an instance of an object." error whenever I make a call to the DB interface in the Account Controller line from below:
var result = _companyaccountrepository.AddToCompanyUsers(model);
Can anyone point out what I'm doing wrong? Thanks in advance.
Account Controller
private ICompanyAccountRepository _companyaccountrepository { get; set; }
public ICompanyAccountRepository companyaccountrepository
{
get { return _companyaccountrepository ?? (_companyaccountrepository = new CompanyAccountRepository()); }
}
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
private ApplicationSignInManager _signInManager;
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set { _signInManager = value; }
}
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, ICompanyAccountRepository companyaccountrepository)
{
UserManager = userManager;
SignInManager = signInManager;
_companyaccountrepository = companyaccountrepository;
}
...
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SignUp(RegisterUserAndCompanyViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
user.FirstName = model.FirstName;
user.LastName = model.LastName;
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
var result = _companyaccountrepository.AddToCompanyUsers(model); //*** THIS IS WHERE THE PROBLEM OCCURS ****
return RedirectToAction("Confirmation");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Interface/Dapper SQL (dummy code to make it simple)
public interface ICompanyAccountRepository
{
CompanyUser AddToCompanyUsers(RegisterUserAndCompanyViewModel user);
}
public class CompanyAccountRepository : ICompanyAccountRepository
{
private string dbconn = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
public bool AddToCompanyUsers(RegisterUserAndCompanyViewModel user);
{
using (SqlConnection cn = new SqlConnection(dbconn))
{
cn.Open();
cn.Insert(new CompanyUser() { CompanyId = user.companyid, UserId = user.id });
cn.Close();
}
return true;
}
}
Unity.Config
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your types here
//12-1-16 Need this for Identity
container.RegisterType<ApplicationDbContext>();
container.RegisterType<ApplicationSignInManager>();
container.RegisterType<ApplicationUserManager>();
container.RegisterType<EmailService>();
container.RegisterType<IAuthenticationManager>(
new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
new InjectionConstructor(typeof(ApplicationDbContext)));
container.RegisterType<AccountController>(
new InjectionConstructor(typeof(ApplicationUserManager), typeof(ApplicationSignInManager), typeof(ICompanyAccountRepository)));
container.RegisterType<AccountController>(
new InjectionConstructor());
//Identity / Unity stuff below to fix No IUserToken Issue - http://stackoverflow.com/questions/24731426/register-iauthenticationmanager-with-unity
//container.RegisterType<DbContext, ApplicationDbContext>(
// new HierarchicalLifetimeManager());
container.RegisterType<UserManager<ApplicationUser>>(
new HierarchicalLifetimeManager());
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
new HierarchicalLifetimeManager());
container.RegisterType<ICompanyAccountRepository, CompanyAccountRepository>();
}
Thanks again for any suggestions.
NOTE: If I add instantiate the repository just before the AddToCompanyUsers call (below), it works fine. However, this breaks Unity/IOC
_companyaccountrepository= new CompanyAccountRepository();
var result = _companyaccountrepository.AddToCompanyUsers(model);
You can try it like this:
(this should fix your repository error. As for your userManager and signInManager, I believe you can improve how they are configured as well, but that will take to take a look on your startup.auth and your ApplicationDbContext and with all the Identity configuration)
Account Controller
private readonly ICompanyAccountRepository _companyaccountrepository;// { get; set; } -- remove the getter and setter here
//remove this
// public ICompanyAccountRepository companyaccountrepository
// {
// get { return _companyaccountrepository ?? (_companyaccountrepository = new CompanyAccountRepository()); }
// }
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
private ApplicationSignInManager _signInManager;
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set { _signInManager = value; }
}
//I think you can remove the parameterless constructor as well
//public AccountController()
//{
//
//}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, ICompanyAccountRepository companyaccountrepository)
{
UserManager = userManager;
SignInManager = signInManager;
_companyaccountrepository = companyaccountrepository;
}
...
EDIT
Change your constructor to:
public AccountController(ICompanyAccountRepository companyaccountrepository)
{
_companyaccountrepository = companyaccountrepository;
}
I try to build a parametered middleware, but I only get http 504 error. This code works fine without the parameter
any idea?
public void Configuration(IAppBuilder app)
{
app.Use<MyMiddleware>("Hello");
}
class MyMiddleware : OwinMiddleware
{
public MyMiddleware(OwinMiddleware next, string message)
: base(next)
{
Message = message;
}
public string Message { get; set; }
public override async Task Invoke(IOwinContext context)
{
context.Response.ContentLength = Message.Length;
context.Response.ContentType = "text/html";
await context.Response.WriteAsync(Message);
await Next.Invoke(context);
}
}
Ok I found the issue
ContentLength should be evaluated in Utf-8 like this:
Encoding.UTF8.GetBytes(Message).Length
c# uses utf-16 encoding by default
I have generic Result<T> generic class which I use often in methods to return result like this
public Result<User> ValidateUser(string email, string password)
There is ILoggingService interface in Result class for logging service injection but I do not find a way to inject actual implementation.
I tried to execute the code below but TestLoggingService intance is not injected into LoggingService property. It always return null. Any ideas how to solve it?
using (var kernel = new StandardKernel())
{
kernel.Bind<ILoggingService>().To<TestLoggingService>();
var resultClass = new ResultClass();
var exception = new Exception("Test exception");
var testResult = new Result<ResultClass>(exception, "Testing exception", true);
}
public class Result<T>
{
[Inject]
public ILoggingService LoggingService{ private get; set; } //Always get null
protected T result = default(T);
//Code skipped
private void WriteToLog(string messageToLog, object resultToLog, Exception exceptionToLog)
{
LoggingService.Log(....); //Exception here, reference is null
}
You are creating the instance manually using new. Ninject will only inject objects created by kernel.Get(). Furthermore it seems you try to inject something into a DTO which is not recommended. Better do the the logging in the class that created the result:
public class MyService
{
public MyService(ILoggingService loggingService) { ... }
public Result<T> CalculateResult<T>()
{
Result<T> result = ...
_loggingService.Log( ... );
return result;
}
}