I have a simple test for the following controller action method
Admin controller
namespace TestingDemo {
public class AdminController : Controller {
private IUserRepository repository;
public AdminController(IUserRepository repo) {
repository = repo;
}
public ActionResult ChangeLoginName(string oldName, string newName) {
User user = repository.FetchByLoginName(oldName);
user.LoginName = newName;
repository.SubmitChanges();
// render some view to show the result
return View();
}
}
}
The test I have is this
namespace TestingDemo.Tests {
[TestClass]
public class AdminControllerTests {
[TestMethod]
public void CanChangeLoginName() {
// Arrange (set up a scenario)
User user = new User() { LoginName = "Bob" };
FakeRepository repositoryParam = new FakeRepository();
repositoryParam.Add(user);
AdminController target = new AdminController(repositoryParam);
string oldLoginParam = user.LoginName;
string newLoginParam = "Joe";
// Act (attempt the operation)
target.ChangeLoginName(oldLoginParam, newLoginParam);
// Assert (verify the result)
Assert.AreEqual(newLoginParam, user.LoginName);
Assert.IsTrue(repositoryParam.DidSubmitChanges);
}
}
class FakeRepository : IUserRepository {
public List<User> Users = new List<User>();
public bool DidSubmitChanges = false;
public void Add(User user) {
Users.Add(user);
}
public User FetchByLoginName(string loginName) {
return Users.First(m => m.LoginName == loginName);
}
public void SubmitChanges() {
DidSubmitChanges = true;
}
}
}
my question is how does user.LoginName get changed? the only change that happens to user is in the action method, and that is a local user variable. How is the user variable in the test getting updated?
The controller test first adds a user to List<User> Users in the fake repository, using the Users.Add method.
The repository this is added to is passed into the constructor of the controller in the test:
AdminController target = new AdminController(repositoryParam);
Related
I am doing the management of a user's account when necessary I can Lock a user's account in case they violate it. Or can be unlocked if required. I got an error like this. Where am I wrong, I use .Net Core 5 to build my program. Error: "An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object."
enter image description here
Interface
public bool LockUser(string email);
public bool UnlockUser(string email);
Repo
public bool LockUser(string email)
{
var userTask = _userManager.FindByEmailAsync(email);
userTask.Wait();
var user = userTask.Result;
var lockUserTask = _userManager.SetLockoutEnabledAsync(user, true);
lockUserTask.Wait();
var lockDateTask = _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now);
lockDateTask.Wait();
return lockDateTask.Result.Succeeded && lockUserTask.Result.Succeeded;
}
Controller
public ActionResult LockUser(string email)
{
if (!_userRepository.LockUser(email))
{
throw new ArgumentException("Error");
}
return RedirectToAction("Index");
}
Please refer the following sample code, the UserRepository should like this, add the usermanager via the constructor parameter:
public interface IUserRepository
{
public bool LockUser(string email);
public bool UnlockUser(string email);
}
public class UserRepository : IUserRepository
{
private readonly UserManager<IdentityUser> _userManager;
public UserRepository(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public bool LockUser(string email)
{
var userTask = _userManager.FindByEmailAsync(email);
userTask.Wait();
var user = userTask.Result;
var lockUserTask = _userManager.SetLockoutEnabledAsync(user, true);
lockUserTask.Wait();
var lockDateTask = _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now);
lockDateTask.Wait();
return lockDateTask.Result.Succeeded && lockUserTask.Result.Succeeded;
}
public bool UnlockUser(string email)
{
//...
throw new NotImplementedException();
}
}
Then, add the service to the service container:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddScoped<IUserRepository, UserRepository>();
services.AddControllersWithViews();
}
Then, in the MVC controller:
public class HomeController : Controller
{
private readonly IUserRepository _userRepository;
public HomeController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public IActionResult Index(int id)
{
string email = "aa#hotmail.com";
if (!_userRepository.LockUser(email))
{
throw new ArgumentException("Error");
}
return View();
}
The debug screenshot like this:
I have added a few custom claims to my user and I was wondering if I want to check if these claims exist on controller actions using attributes, I know that we can create a class and extend attribute from .Net and the general idea is to check if user has claim or not, I'm not really clear on the implementation.
Maybe something like this:
[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
public class ClaimRequirementAttribute : Attribute
{
public ClaimRequirementAttribute(string claimType)
{
new Claim(claimType, null);
}
}
public class ClaimRequirementFilter
{
public void OnAuthorization(HttpContext httpContext)
{
var hasClaim = httpContext.User.HasClaim(x => x.Type ==
CapabilityClaims.CanReadSpore);
if (!hasClaim)
{
}
}
}
You can get the Claims of a specific user, using the GetClaimsAsync method of UserManager.
You can use the following method:
public class TestController : Controller
{
private readonly UserManager<AppUser> _userManager;
public TestController(UserManager<AppUser> userManager)
{
_userManager = userManager;
}
public CheckIfClaimsExist(string email)
{
var user = await _userManager.FindByEmailAsync(email);
if(user != null)
{
var claims = await _userManager.GetClaimsAsync(user);
}
}
}
Note: AppUser class is a custom class which extends IdentityUser class from identity server.
After some long research i found this answer using filters
which ended up being the best approach
[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)]
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(params string[] claimType) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] { claimType };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly string[] _claimTypes;
public ClaimRequirementFilter(string[] claimTypes)
{
_claimTypes = claimTypes;
}
public void OnAuthorization(AuthorizationFilterContext authContext)
{
if (authContext == null)
{
throw new ArgumentNullException(nameof(authContext));
}
var user = authContext.HttpContext.User;
var resourceId = authContext.RouteData.Values["id"].ToString();
var claimType = _claimTypes
.All(s => (user.Claims)
.Any(c => c.Type == s && (c.Value == resourceId || c.Value == string.Empty)));
if (user == null || !claimType)
{
authContext.Result = new ForbidResult();
}
}
At present, I am writing unit tests for my controller. Below is the structure of my code in the project.
MyController Class
public class MyController : Controller
{
private readonly MyRepository _myRepository;
public MyController()
: this(new MyRepository())
{
}
[HttpGet]
public ActionResult Index()
{
var items = _myRepository.GetAllItems();
if (items.Count() == 0)
return View("EmptyItems");
else
{
return View("List", items);
}
}
}
MyRepository Class
public class MyRepository : IDisposable, IMyRepository
{
private readonly MyDbContext _dbcontext;
private readonly ISecurityService _securityService;
public TodoListItemsRepository() : this(new MyDbContext(), new SecurityService())
{
}
public TodoListItemsRepository(MyDbContext context, ISecurityService securityService)
{
_dbcontext = context;
_securityService = securityService;
}
public IEnumerable<MyModel> GetAllItems()
{
var userid = _securityService.GetUser();
var todoList = _dbcontext.MyList.Where(e => e.UserId == userid);
return todoList;
}
//Other Methods etc...
......
}
SecurityService class
public class SecurityService : ISecurityService
{
public int GetUser()
{
return (int)Membership.GetUser().ProviderUserKey;
}
}
Here all methods inside my repository depends on GetUser method. Hence, I have initialized it inside the constructor. The repository class is initialized from the controller constructor.
My issue is - I couldn't unit the Index action unless I need to initialize dbcontext and the securityservice. Could someone please advise me if I am doing the right thing or any changes required in the structure of my code so that I can unit test my application ? I am new to MVC. So, any suggestions would be much appreciated.
I want to implement a redirect unauthorized user with a check in the proper attribute. To do this I create a class attribute with a constructor with no parameters.
[AttributeUsage(AttributeTargets.Method)]
public class LoggedAttribute:Attribute
{
public LoggedAttribute()
{
//TODO
}
}
Now assign this attribute to all methods of action that requires authorization.
[Logged]
public ViewResult SendMessage()
{
return View();
}
I have a User model with boolean flag IsLoggedIn. How can I check this flag in the class attribute to redirect the user to the authentication page in case of an emitted flag ?
In the case of using a custom authorization attribute like below:
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
//anything else you'd like to do like log it
return false;
}
}
}
and then you can redirect them by the following override:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//disable the redirect
if(disabled)
{
//do something else
}else{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Account",
action = "Login"
})
);
}
}
UPDATE: and you use it like this:
[AuthorizeUser]
public ActionResult myAction()
{
return View();
}
I want to have my own AppContext in my ApiController (MVC4).
Should be something like
public class TestController : BaseApiController
{
[HttpGet]
public IEnumerable<TestVM> GetAll()
{
// the test service is injected with SimpleInjector
return _testService.GetAll(**base.AppContext**);
}
}
but the ApiController haven't access to the Session.
Are there any solutions to "activate" the Session for specific keys (because I don't want the whole Session)?
Or do you have any other idea (cache or cookie)?
This is the BaseApiController
public abstract class BaseApiController: ApiController
{
public IAppContext AppContext
{
get { return SessionState.AppContext; }
}
}
and this is my IAppContext (it will have more properties in the future)
public interface IAppContext
{
IIdentity User { get; }
/// <summary> Gets the user id. </summary>
/// <value>The user id.</value>
int IdUser { get; }
}
here the application module which is registered in the web.config
public class ApplicationModule : IHttpModule
{
// ...
SessionState.AppContext = _appContext.InitializeNew(
HttpRuntime.AppDomainAppPath, languages);
// ...
}
SessionState class to get the AppContext
public class SessionState : BaseSessionVariables
{
public static IAppContext AppContext
{
get { return SessionState.Get<IAppContext>("AppContext"); }
set { SessionState.Set("AppContext", value); }
}
}
here the BaseSessionVariables class
public static HttpSessionState GetSession()
{
return HttpContext.Current.Session;
}
protected static T Get<T>(string key) where T : class
{
var session = BaseSessionVariables.GetSession();
if (session == null)
{
throw new Exception("No session");
}
return (session[key] as T);
}
Thanks for your help!
Take a look at the implementation below. It should get you headed in the right direction.
Updated IAppContext - Added Setters
public interface IAppContext
{
IIdentity User { get; set; }
/// <summary> Gets the user id. </summary>
/// <value>The user id.</value>
int IdUser { get; set; }
}
Updated Base Controller - Instantiates a new AppContextImplemenation in the OnActionExecuting method
public abstract class BaseApiController: ApiController
{
public IAppContext AppContext {get; set;}
protected override void OnActionExecuting(
ActionExecutingContext filterContext)
{
AppContext = new AppContextImplementation();
}
}
New Class - implements IAppContext and wraps the HttpContext Session. For testing you can then create an TestAppContextImplementation that doesn't rely on Session but some other in memory storage mechanism.
public class AppContextImplementation : IAppContext
{
public IIdentity User
{
get { return HttpContext.Current.Session["User"] as IIdentity; }
set { HttpContext.Current.Session["User"] = value; }
}
int IdUser
{
get { return Convert.ToInt32(Session["IdUser"]); }
set { Session["IdUser"] = value; }
}
}
For ApiControllers, build yourself a DelegatingHandler and push all of your goodies onto request.Properties. You can then retrieve them from your request whether you are testing or running live. The benefit is that you then have zero dependency on Session in your Controller.
MessageHandler
public class ContextHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// get the goodies to add onto the request
var goodies = /* call to goodieGoodieYumYum */
// add our goodies onto the request
request.Properties.Add(Constants.RequestKey_Goodies, goodies);
// pass along to the next handler
return base.SendAsync(request, cancellationToken);
}
}
Controller Action
var goodies = (List<Goodie>)Request.Properties[Constants.RequestKey_Goodies];