MVC4 - during post controller action, the WebSecurity.CurrentUserId is losing it's value, and becomes -1 - asp.net-mvc-4

Somehow, in this controller, after the SaveChanges, the CurrentUserId becomes -1.
The data post works, and the CurrentUserId has it's logged in value (example 8888), but after the SQL insert, the WebSecurity.CurrentUserId becomes -1. Any clue? During debug I can't find where and why.
// POST: /Account/Edit
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(UserProfile model)
{
if (ModelState.IsValid)
{
using (var context = new dbContext())
{
var id = WebSecurity.CurrentUserId;
var account = context.UserProfiles.Find(id);
UpdateModel(account);
context.SaveChanges();
return RedirectToAction("Index", "Account");
}
}
else
{
return View(model);
}
}

that will always return -1, what you need is the below code
int currentuserid = WebSecurity.GetUserId(username);
You can then validate that the userid above, matches the userid in the model, in order to prevent users, changing other users code
as Additional. I use this in my Base Controller:
public int GetUserId()
{
var userid = "0";
if (Request.IsAuthenticated && User.Identity.Name != null)
{
var membershipUser = Membership.GetUser(User.Identity.Name);
if (membershipUser != null)
{
if (membershipUser.ProviderUserKey != null)
{
userid = membershipUser.ProviderUserKey.ToString();
}
}
}
return Convert.ToInt32(userid);
}

Related

How to call action result return json on another action result on asp.net core 2.2?

Problem
How to call Action Result on another Action Result ?
I have two Action Result PostUserLogins and Action Result GetBranches
Can I call ActionResult getbranches inside ActionResult postlogin ?
[HttpPost(Contracts.ApiRoutes.Login.UserLogin)]
public IActionResult PostUserLogins([FromBody] Users user)
{
if (LoginStatus == 1)
{
// for Invalid Username Or password
dynamic request_status = new JObject();
request_status.Status = "failed";
request_status.Code = LoginStatus;
request_status.Message = errorMessage;
request_status.Branches = ????? How to call GetBranches Action;
// call action result to get GetBranches(Users user) as json;
JsonResults = "request_status" + JsonConvert.SerializeObject(request_status);
}
}
[HttpGet(Contracts.ApiRoutes.Login.GetBranches)]
public IActionResult GetBranches([FromRoute] string UserId)
{
List<Branches> branchesList = new List<Branches>();
for (int i = 0; i < dtBranches.Rows.Count; i++)
{
Branches branch = new Branches();
branch.BranchCode = Utilities.ObjectConverter.ConvertToInteger(dtBranches.Rows[i]["BranchCode"]);
branch.BranchName = Utilities.ObjectConverter.ConvertToString(dtBranches.Rows[i]["BranchAraName"]);
branchesList.Add(branch);
}
JsonResults = "request_status" + JsonConvert.SerializeObject(branchesList);
return Ok(JsonResults);
}
Regardless whether you could or not, you shouldn't.
The simplest way is to extract that logic to another method:
[HttpPost(Contracts.ApiRoutes.Login.UserLogin)]
public IActionResult PostUserLogins([FromBody] Users user)
{
if (LoginStatus == 1)
{
// for Invalid Username Or password
dynamic request_status = new JObject();
request_status.Status = "failed";
request_status.Code = LoginStatus;
request_status.Message = errorMessage;
request_status.Branches = GetBrancesImpl();
JsonResults = "request_status" + JsonConvert.SerializeObject(request_status);
}
}
[HttpGet(Contracts.ApiRoutes.Login.GetBranches)]
public IActionResult GetBranches([FromRoute] string UserId)
{
JsonResults = "request_status" + JsonConvert.SerializeObject(GetBrancesImpl());
return Ok(JsonResults);
}
private IEnumerable<Branches> GetBrancesImpl()
{
from branch in dtBranches.Rows
select new new Branches
{
BranchCode = Utilities.ObjectConverter.ConvertToInteger(dtBranches.Rows[i]["BranchCode"]),
BranchName = Utilities.ObjectConverter.ConvertToString(dtBranches.Rows[i]["BranchAraName"]),
};
}
Best would be to move this logic to a service class that holds the logic and can easily be tested.
If they are in the same controller,you could call it directly in PostUserLogins like:
public IActionResult PostUserLogins([FromBody] Users user)
{
//other logic
var result = GetBranches("myUserID") as OkObjectResult;
var json = result.Value.ToString().Substring(14);//remove the first "request_status" in the string to make it a valid json be deserialized later
request_status.Branches = JsonConvert.DeserializeObject<List<Branch>>(json);//get the Branch list
JsonResults = "request_status" + JsonConvert.SerializeObject(request_status);
}

How to bind a model to a session in ASP MVC Core

I bind the model to a session in ASP MVC Framework like this:
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
Cart cart = null;
if(controllerContext.HttpContext.Session != null)
{
cart = (Cart)controllerContext.HttpContext.Session[sessionKey];
}
if(cart == null)
{
cart = new Cart();
if (controllerContext.HttpContext.Session != null)
{
controllerContext.HttpContext.Session[sessionKey] = cart;
}
}
return cart;
}
Now I want to do the same thing in ASM MVC Core, and this was my attempt:
public Task BindModelAsync(ModelBindingContext bindingContext)
{
Cart cart = null;
if (bindingContext.HttpContext.Session != null)
{
cart = (Cart)JsonConvert.DeserializeObject(bindingContext.HttpContext.Session.GetString(sessionKey));
}
if (cart == null)
{
cart = new Cart();
if (bindingContext.HttpContext.Session != null)
{
bindingContext.HttpContext.Session.SetString(sessionKey, JsonConvert.SerializeObject(cart));
}
}
return Task.CompletedTask;
}
I also have the class for model binder provider.
But I get a run-time error on this line, saying that the object is null:
cart = (Cart)JsonConvert.DeserializeObject(bindingContext.HttpContext.Session.GetString(sessionKey));
The string returned from 'GetString(sessionKey)' is null. The full message is:
System.ArgumentNullException: 'Value cannot be null. Parameter name: value''.
The question doesn't mention what exception is thrown, but this code is guaranteed to fail the first time an attempt is made to read from the session.
The second snippet tries to deserialize a string without checking whether it's null or not :
cart=(Cart)JsonConvert.DeserializeObject(bindingContext.HttpContext.Session.GetString(sessionKey));
Or, in a more readable way:
var json=bindingContext.HttpContext.Session.GetString(sessionKey);
cart = (Cart)JsonConvert.DeserializeObject(json);
JsonConvert.DeserializeObject() will throw if its argument is null.
The json string must be checked before calling DeserializeObject. With some cleanup, the code could look like this:
var session=bindingContext.HttpContext.Session;
if(session == null)
{
return null;
}
var json = sessio.GetString(sessionKey);
if (!String.IsNullOrWhitespace(json))
{
var cart=JsonConvert.DeserializeObject<Cart>(json);
return cart;
}
else
{
var emptyCart=new Cart();
var value=JsonConvert.SerializeObject(emptyCart);
session.SetString(sessionKey, value);
return emptyCart;
}
The null safe operator can be used to handle missing context values, eg during testing :
var session=bindingContext?.HttpContext?.Session;
This will return null if any of the objects is null.

.net core - How to return 403 on AuthorizationHandler?

I implemented my custom AuthorizationHandler.
On that i check i the user can resolved and is active.
If the user isn't active then i would like to return an 403 status.
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidUserRequirement requirement)
{
var userId = context.User.FindFirstValue( ClaimTypes.NameIdentifier );
if (userId != null)
{
var user = await _userManager.GetUserAsync(userId);
if (user != null)
{
_httpContextAccessor.HttpContext.AddCurrentUser(user);
if (user.Active)
{
context.Succeed(requirement);
return;
}
else
{
_log.LogWarning(string.Format("User ´{1}´ with id: ´{0} isn't active", userId, user.UserName), null);
}
}
else
{
_log.LogWarning(string.Format("Can't find user with id: ´{0}´", userId), null);
}
} else
{
_log.LogWarning(string.Format("Can't get user id from token"), null);
}
context.Fail();
var response = _httpContextAccessor.HttpContext.Response;
response.StatusCode = 403;
}
But i receive a 401. Can you please help me?
Could you check that on the end of your function? I'm using that in my custom middleware to rewrite status code to 401 in some cases but in your scenario should also work
var filterContext = context.Resource as AuthorizationFilterContext;
var response = filterContext?.HttpContext.Response;
response?.OnStarting(async () =>
{
filterContext.HttpContext.Response.StatusCode = 403;
//await response.Body.WriteAsync(message, 0, message.Length); only when you want to pass a message
});
According to the Single Responsibility Principle , we should not use the HandleRequirementAsync() method to redirect reponse , we should use middleware or Controller to do that instead . If you put the redirect logic in HandleRequirementAsync() , how about if you want to use it in View page ?
You can remove the redirection-related code to somewhere else (outside) , and now you inject an IAuthorizationService to authorize anything as you like , even a resource-based authorization :
public class YourController : Controller{
private readonly IAuthorizationService _authorizationService;
public YourController(IAuthorizationService authorizationService)
{
this._authorizationService = authorizationService;
}
[Authorize("YYY")]
public async Task<IActionResult> Index()
{
var resource /* = ... */ ;
var x = await this._authorizationService.AuthorizeAsync(User,resource , "UserNameActiveCheck");
if (x.Succeeded)
{
return View();
}
else {
return new StatusCodeResult(403);
}
}
}
in .NET core 6.0 you can use the Fail method
AuthorizationHandlerContext.Fail Method
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AppAuthorizationRequirement requirement)
{
context.Fail(); //Use this
}

entity framework 5 change log how to implement?

I am creating an application with MVC4 and entity framework 5. How do can I implement this?
I have looked around and found that I need to override SaveChanges .
Does anyone have any sample code on this? I am using code first approach.
As an example, the way I am saving data is as follows,
public class AuditZoneRepository : IAuditZoneRepository
{
private AISDbContext context = new AISDbContext();
public int Save(AuditZone model, ModelStateDictionary modelState)
{
if (model.Id == 0)
{
context.AuditZones.Add(model);
}
else
{
var recordToUpdate = context.AuditZones.FirstOrDefault(x => x.Id == model.Id);
if (recordToUpdate != null)
{
recordToUpdate.Description = model.Description;
recordToUpdate.Valid = model.Valid;
recordToUpdate.ModifiedDate = DateTime.Now;
}
}
try
{
context.SaveChanges();
return 1;
}
catch (Exception ex)
{
modelState.AddModelError("", "Database error has occured. Please try again later");
return -1;
}
}
}
There is no need to override SaveChanges.
You can
Trigger Context.ChangeTracker.DetectChanges(); // may be necessary depending on your Proxy approach
Then analyze the context BEFORE save.
you can then... add the Change Log to the CURRENT Unit of work.
So the log gets saved in one COMMIT transaction.
Or process it as you see fit.
But saving your change log at same time. makes sure it is ONE Transaction.
Analyzing the context sample:
I have a simple tool, to Dump context content to debug output so when in debugger I can use immediate window to check content. eg
You can use this as a starter to prepare your CHANGE Log.
Try it in debugger immediate window. I have FULL dump on my Context class.
Sample Immediate window call. UoW.Context.FullDump();
public void FullDump()
{
Debug.WriteLine("=====Begin of Context Dump=======");
var dbsetList = this.ChangeTracker.Entries();
foreach (var dbEntityEntry in dbsetList)
{
Debug.WriteLine(dbEntityEntry.Entity.GetType().Name + " => " + dbEntityEntry.State);
switch (dbEntityEntry.State)
{
case EntityState.Detached:
case EntityState.Unchanged:
case EntityState.Added:
case EntityState.Modified:
WriteCurrentValues(dbEntityEntry);
break;
case EntityState.Deleted:
WriteOriginalValues(dbEntityEntry);
break;
default:
throw new ArgumentOutOfRangeException();
}
Debug.WriteLine("==========End of Entity======");
}
Debug.WriteLine("==========End of Context======");
}
private static void WriteCurrentValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.CurrentValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.CurrentValues[cv]);
}
}
private static void WriteOriginalValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.OriginalValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.OriginalValues[cv]);
}
}
}
EDIT: Get the changes
I use this routine to get chnages...
public class ObjectPair {
public string Key { get; set; }
public object Original { get; set; }
public object Current { get; set; }
}
public virtual IList<ObjectPair> GetChanges(object poco) {
var changes = new List<ObjectPair>();
var thePoco = (TPoco) poco;
foreach (var propName in Entry(thePoco).CurrentValues.PropertyNames) {
var curr = Entry(thePoco).CurrentValues[propName];
var orig = Entry(thePoco).OriginalValues[propName];
if (curr != null && orig != null) {
if (curr.Equals(orig)) {
continue;
}
}
if (curr == null && orig == null) {
continue;
}
var aChangePair = new ObjectPair {Key = propName, Current = curr, Original = orig};
changes.Add(aChangePair);
}
return changes;
}
edit 2 If you must use the Internal Object tracking.
var context = ???// YOUR DBCONTEXT class
// get objectcontext from dbcontext...
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
// for each tracked entry
foreach (var dbEntityEntry in context.ChangeTracker.Entries()) {
//get the state entry from the statemanager per changed object
var stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(dbEntityEntry.Entity);
var modProps = stateEntry.GetModifiedProperties();
Debug.WriteLine(modProps.ToString());
}
I decompiled EF6 . Get modified is indeed using private bit array to track fields that have
been changed.
// EF decompiled source..... _modifiedFields is a bitarray
public override IEnumerable<string> GetModifiedProperties()
{
this.ValidateState();
if (EntityState.Modified == this.State && this._modifiedFields != null)
{
for (int i = 0; i < this._modifiedFields.Length; ++i)
{
if (this._modifiedFields[i])
yield return this.GetCLayerName(i, this._cacheTypeMetadata);
}
}
}

MVC4 add overload to a method

As many of you know MVC4 has some great new features, I am struggling with the ContextDependentView trying to add an overload to it. I get an error saying no overload method for ContextDependentView takes 1 argument . My original code that was working was this
// This worked fine
return View(new ModelSample { getInfo= info, Retrieving= Retrieve })
// This is now what I have tried to do that doesn't work
return ContextDependentView(new ModelSample { getInfo= info, Retrieving= Retrieve })
//This is the method for ContextDependentView()
private ActionResult ContextDependentView()
{
string actionName = ControllerContext.RouteData.GetRequiredString("action");
if (Request.QueryString["content"] != null)
{
ViewBag.FormAction = "Json" + actionName;
return PartialView();
}
else
{
ViewBag.FormAction = actionName;
return View();
}
}
I obviously see that there are no overloads but how can I add an overload to the ContextDependentView method to accept my model like return View()..thanks
Add this overload to your controller:
private ActionResult ContextDependentView(SampleModel model)
{
string actionName = ControllerContext.RouteData.GetRequiredString("action");
if (Request.QueryString["content"] != null)
{
ViewBag.FormAction = "Json" + actionName;
return PartialView();
}
else
{
ViewBag.FormAction = actionName;
return View();
}
}
That should work...