Stop AspNet Identity storing email address after validation error - c#

I have a serivce that fetches a list of users from a legacy system and synchronises my AspNet Identity database. I’ve a problem when updating a user’s email address with UserManager.SetEmail(string userId, string email) and the validation fails. The user object in the UserStore retains the value of the invalid email address. I stop processing that user and skip to the next user in my list. Later when my service finds a new user to create, I use UserManager.Create(ApplicationUser user) and the database is updated with all outstanding changes including the invalid email address of the existing user.
Is there a way to stop the invalid email address being persisted? Is this a bug or am I just not using it correctly? Should I just manually take a backup of every object before any update and revert all values if the IdentityResult has an error?
//get LegacyUsers
foreach (AppUser appUser in LegacyUsers){
var user = UserManager.FindByName(appUser.userName);
if (user != null){
If (!user.Email.Equals(appUser.Email)){
var result = UserManager.setEmail(user.Id, appUser.Email)
if (!result.Succeeded){
//user object still has new value of email despite error, but not yet persisted to DB.
Log.Error(…);
continue;
}
}
}
else
{
ApplicationUser newUser = new ApplicationUser{
UserName = appUser.userName,
//etc
}
var result = UserManager.Create(newUser); //DB updates first user with new email aswell as inserting this new user
if (!result.Succeeded){
Log.Error(…);
continue;
}
}
}
I'm using version 2.2.1.40403 of Microsoft.AspNet.Identity.Core and Microsoft.AspNet.Identity.EntityFramework

This is happening because EF keeps track of models and updates all of the modified objects when SaveChanges() method is called by UserManager.Create() method. You could easily detach the user which has invalid email from the DbContext like this:
// first get DbContext from the Owin.
var context = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
foreach (AppUser appUser in LegacyUsers){
var user = UserManager.FindByName(appUser.userName);
if (user != null){
If (!user.Email.Equals(appUser.Email)){
var result = UserManager.setEmail(user.Id, appUser.Email)
if (!result.Succeeded){
Log.Error(…);
// detach the user then proceed to the next one
context.Entry(user).State = EntityState.Detached;
continue;
}
}
}
else{
// rest of the code
}
}

Related

Error Account with Id = "xxxxxx" does not exist

I have a custo workflow that creates an account and opportunities.
Sometimes I have this error: Account with Id = "xxxxxx" does not exist.
I don't know what's wrong in my code knowing that I find the account in the CRM.
Here are the steps of my plugin code:
Find the account by num (if it doesn't exist, I create them)
Get the account = Account
Create an opportunity with Opportunity["parentaccountid"] = Account;
Error message !
Code:
//Get opportunity
Guid id = retrieveOpportunity<string>("opportunity", "new_numero", numero, service);
Entity eOpportunity;
if (id != Guid.Empty)
{
eOpportunity = new Entity("opportunity", id);
}
else
{
eOpportunity = new Entity("opportunity");
}
//Get account
EntityReference eAccount = retrieveAccount<string>(accountCode, "account", "new_code", service);
if (eAccount == null)
{
eAccount = new Entity("account", "new_code", accountCode);
eAccount["name"] = "name";
UpsertRequest usMessage = new UpsertRequest()
{
Target = eAccount
};
//create account
UpsertResponse usResponse = (UpsertResponse)this._service.Execute(usMessage);
eOpportunity["parentaccountid"] = usResponse.Target;
}
else
{
eOpportunity["parentaccountid"] = eAccount;
}
UpsertRequest req = new UpsertRequest()
{
Target = eOpportunity
};
//upsert opportunity
UpsertResponse resp = (UpsertResponse)service.Execute(req);
if (resp.RecordCreated)
tracer.Trace("New opportunity");
else
tracer.Trace("Opportunity updated");
Sometimes there are several workflows that are started at the same time and that do the same thing (creating other opportunities)
You haven't shown us the entire plugin, so this is just a guess, but you're probably sharing your IOrganizationService at the class level, which is causing race conditions in your code, and one thread creates a new account in a different context, then its service gets overwritten by another thread, which is in a different database transaction that doesn't have the newly created account and it's erroring.
Don't share your IOrganziationService across threads!
Whenever you are trying to consume the created record in the same transaction, convert the plugin into Asynchronous mode - this will work.

Return user details after they have logged in

the code below works ok, just one issue, the code confirms if user logged in is Admin or not i.e. the code checks if the user name is within the AspNetUser table and returns a Boolean true or false.
But I also need the user GarageID to be returned, the GarageID field is held within the AspNetUser table any ideas how to do this?
private bool IsUserAdmin()
{
System.Security.Principal.WindowsIdentity identity = Context.Request.LogonUserIdentity;
//Debug.WriteLine(identity.Name);
string loginName = identity.Name;
//Debug.WriteLine(loginName);
TyrescannerWebApp.IdentityModel.tyrescannerdatabaseEntities dbcontext = new TyrescannerWebApp.IdentityModel.tyrescannerdatabaseEntities();
return content.AdminUsers.Any(a => a.LoginName == loginName);
}
You should use the role provider to determine if the user is in the current role.
if (System.Web.Security.Roles.IsUserInRole(loginName, "Admin"))
{
//Do something
}
else
{
//Display unauthorized message
}

Assigning multiple roles to user in DNN programmatically

I am trying to programmatically assign multiple roles to a user in DNN.
Using the following code :
Roles_controller.AddUserRole(0, user_id, role_id, DateTime.Now, DateTime.Now.AddYears(10));
DotNetNuke.Common.Utilities.DataCache.ClearUserCache(0, user_name);
Roles_controller.ClearRoleCache(0);
The problem is, no matter how many roles I assign to the user, only the last role that I assigned to the user is actually assigned. The previous assignment of roles to users were ignored. No Errors where thrown..
How can I achieve this ? Is there any limitation in DNN to do this ?
This is a method that I wrote to add a role to a user. It works for me to call it multiple times, even in a loop.
public bool AddRoleToUser(int portalid, UserInfo user, string roleName, DateTime expiry)
{
bool rc = false;
if (user != null)
{
var roleCtl = new RoleController();
RoleInfo newRole = roleCtl.GetRoleByName(portalid, roleName);
if (newRole != null)
{
roleCtl.AddUserRole(portalid, user.UserID, newRole.RoleID, DateTime.MinValue, expiry);
// Refresh user and check if role was added
user = UserController.GetUserById(portalid, user.UserID);
rc = user.IsInRole(roleName);
}
}
return rc;
}
Maybe it was the call to refresh the user that refreshed it from the cache.

Add a user to parse role

I'm trying to create a new user and adding it to an already existing ParseRole, but i'm getting the ParseException: object not found for update after performing await parseRole.SaveAsync();.
I checked in the Parse website and both the Role and User are saved (the ids returned by c# code are correct), but the Role hasn't that user.
[TestMethod]
public async Task CanCreateCustomer()
{
var customer = ParseObject.Create<ApplicationUser>();
customer.FirstName = GetRandom.FirstName();
customer.LastName = GetRandom.LastName();
customer.Password = "123";
customer.Username = customer.GetUserName();
Assert.IsNull(customer.ObjectId);
await customer.SignUpAsync();
Assert.IsNotNull(customer.ObjectId);
var parseRole = await ParseRole.Query.Where(x => x.Name == Roles.CustomerRole).FirstAsync();
parseRole.Users.Add(customer);
await parseRole.SaveAsync();
}
The problem was I didn't set the ACL for my roles to allow write access to the logged user (or public, in case of my unit test)

How to get the current user with Signalr?

I tried this, but the user always ends up being null:
ApplicationDbContext db = new Models.ApplicationDbContext();
var user = db.Users.Find(Context.User.Identity.Name);
if (user == null)
{
caller.displayMessage("Error: User does not exist.");
return;
}
So, how do I get the current user? I need to get the user's Id and email address.
Find in EF uses the argument as the key value. I don't think the Name property is the key property and therefore you always get null.

Categories