diff --git a/server/AyaNova/biz/UserBiz.cs b/server/AyaNova/biz/UserBiz.cs index 9a72ea05..41aa432b 100644 --- a/server/AyaNova/biz/UserBiz.cs +++ b/server/AyaNova/biz/UserBiz.cs @@ -153,19 +153,24 @@ namespace AyaNova.Biz //put internal bool Put(User dbObj, User inObj) { - //Replace the db object with the PUT object skipping the password and salt and Id fields - CopyObject.Copy(inObj, dbObj, "Id, Salt, Password"); + //Get a snapshot of the original db value object before changes + User SnapshotObj = new User(); + CopyObject.Copy(dbObj, SnapshotObj); + + //Update the db object with the PUT object values + CopyObject.Copy(inObj, dbObj, "Id, Salt"); //Is the user updating the password? - if (!string.IsNullOrWhiteSpace(inObj.Password) && dbObj.Password != inObj.Password) + if (!string.IsNullOrWhiteSpace(inObj.Password) && SnapshotObj.Password != inObj.Password) { //YES password is being updated: - inObj.Password = Hasher.hash(inObj.Salt, inObj.Password); + dbObj.Password = Hasher.hash(SnapshotObj.Salt, inObj.Password); } else { - //No, use the db password value - //Should not require any code to run as it will retain it's db value + //No, use the snapshot password value + dbObj.Password = SnapshotObj.Password; + dbObj.Salt = SnapshotObj.Salt; } @@ -173,7 +178,7 @@ namespace AyaNova.Biz //this will allow EF to check it out ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = inObj.ConcurrencyToken; - Validate(dbObj, inObj); + Validate(dbObj, SnapshotObj); if (HasErrors) return false; @@ -183,15 +188,20 @@ namespace AyaNova.Biz //patch internal bool Patch(User dbObj, JsonPatchDocument objectPatch, uint concurrencyToken) { - //TODO: objectPatch handle patching password - - //make a snapshot of the original for validation but update the original to preserve workflow - - User snapshotObj=null; + //make a snapshot of the original for validation but update the original to preserve workflow + User snapshotObj = new User(); CopyObject.Copy(dbObj, snapshotObj); //Do the patching objectPatch.ApplyTo(dbObj); + + //Is the user patching the password? + if (!string.IsNullOrWhiteSpace(dbObj.Password) && dbObj.Password != snapshotObj.Password) + { + //YES password is being updated: + dbObj.Password = Hasher.hash(dbObj.Salt, dbObj.Password); + } + ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken; Validate(dbObj, snapshotObj); if (HasErrors) @@ -237,7 +247,7 @@ namespace AyaNova.Biz //run validation and biz rules bool isNew = currentObj == null; - + if (isNew) //Yes, no currentObj { //Not sure why we would care about this particular rule or why I added it? Maybe it's from widget? diff --git a/server/AyaNova/util/CopyObject.cs b/server/AyaNova/util/CopyObject.cs index ae72c8e4..209600a1 100644 --- a/server/AyaNova/util/CopyObject.cs +++ b/server/AyaNova/util/CopyObject.cs @@ -22,7 +22,10 @@ namespace AyaNova.Util { string[] excluded = null; if (!string.IsNullOrEmpty(excludedProperties)) + { + excludedProperties=excludedProperties.Replace(", ", ",").Replace(" ,",",").Trim(); excluded = excludedProperties.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } MemberInfo[] miT = target.GetType().GetMembers(memberAccess); foreach (MemberInfo Field in miT) diff --git a/test/raven-integration/User/UserCrud.cs b/test/raven-integration/User/UserCrud.cs index 45f537e5..7556e4cc 100644 --- a/test/raven-integration/User/UserCrud.cs +++ b/test/raven-integration/User/UserCrud.cs @@ -15,44 +15,44 @@ namespace raven_integration [Fact] public async void CRUD() { - + //CREATE - dynamic d1 = new JObject(); - d1.name = Util.Uniquify("First Test User"); - d1.ownerId = 1L; - d1.active=true; - d1.login=Util.Uniquify("LOGIN"); - d1.password=Util.Uniquify("PASSWORD"); - d1.roles=0;//norole - d1.localeId=1;//random locale - d1.userType=3;//non scheduleable + dynamic D1 = new JObject(); + D1.name = Util.Uniquify("First Test User"); + D1.ownerId = 1L; + D1.active = true; + D1.login = Util.Uniquify("LOGIN"); + D1.password = Util.Uniquify("PASSWORD"); + D1.roles = 0;//norole + D1.localeId = 1;//random locale + D1.userType = 3;//non scheduleable - ApiResponse r1 = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), d1.ToString()); - Util.ValidateDataReturnResponseOk(r1); - long d1Id = r1.ObjectResponse["result"]["id"].Value(); + ApiResponse R1 = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D1.ToString()); + Util.ValidateDataReturnResponseOk(R1); + long d1Id = R1.ObjectResponse["result"]["id"].Value(); - dynamic d2 = new JObject(); - d2.name = Util.Uniquify("Second Test User"); - d2.ownerId = 1L; - d2.active=true; - d2.login=Util.Uniquify("LOGIN"); - d2.password=Util.Uniquify("PASSWORD"); - d2.roles=0;//norole - d2.localeId=1;//random locale - d2.userType=3;//non scheduleable + dynamic D2 = new JObject(); + D2.name = Util.Uniquify("Second Test User"); + D2.ownerId = 1L; + D2.active = true; + D2.login = Util.Uniquify("LOGIN"); + D2.password = Util.Uniquify("PASSWORD"); + D2.roles = 0;//norole + D2.localeId = 1;//random locale + D2.userType = 3;//non scheduleable - ApiResponse r2 = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), d2.ToString()); - Util.ValidateDataReturnResponseOk(r2); - long d2Id = r2.ObjectResponse["result"]["id"].Value(); + ApiResponse R2 = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D2.ToString()); + Util.ValidateDataReturnResponseOk(R2); + long d2Id = R2.ObjectResponse["result"]["id"].Value(); //RETRIEVE //Get one - ApiResponse r3 = await Util.GetAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in")); - Util.ValidateDataReturnResponseOk(r3); - r3.ObjectResponse["result"]["name"].Value().Should().Be(d2.name.ToString()); + ApiResponse R3 = await Util.GetAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in")); + Util.ValidateDataReturnResponseOk(R3); + R3.ObjectResponse["result"]["name"].Value().Should().Be(D2.name.ToString()); @@ -60,16 +60,16 @@ namespace raven_integration //PUT //update w2id - d2.name = Util.Uniquify("UPDATED VIA PUT SECOND TEST User"); - d2.OwnerId = 1; - d2.concurrencyToken = r2.ObjectResponse["result"]["concurrencyToken"].Value(); - ApiResponse PUTTestResponse = await Util.PutAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), d2.ToString()); + D2.name = Util.Uniquify("UPDATED VIA PUT SECOND TEST User"); + D2.OwnerId = 1; + D2.concurrencyToken = R2.ObjectResponse["result"]["concurrencyToken"].Value(); + ApiResponse PUTTestResponse = await Util.PutAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), D2.ToString()); Util.ValidateHTTPStatusCode(PUTTestResponse, 200); //check PUT worked ApiResponse checkPUTWorked = await Util.GetAsync("User/" + d2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in")); Util.ValidateNoErrorInResponse(checkPUTWorked); - checkPUTWorked.ObjectResponse["result"]["name"].Value().Should().Be(d2.name.ToString()); + checkPUTWorked.ObjectResponse["result"]["name"].Value().Should().Be(D2.name.ToString()); uint concurrencyToken = PUTTestResponse.ObjectResponse["result"]["concurrencyToken"].Value(); //PATCH @@ -89,7 +89,7 @@ namespace raven_integration } - + //TODO: Test password updating code @@ -101,8 +101,8 @@ namespace raven_integration { //Get non existant //Should return status code 404, api error code 2010 - ApiResponse a = await Util.GetAsync("User/999999", await Util.GetTokenAsync("manager", "l3tm3in")); - Util.ValidateResponseNotFound(a); + ApiResponse R = await Util.GetAsync("User/999999", await Util.GetTokenAsync("manager", "l3tm3in")); + Util.ValidateResponseNotFound(R); } /// @@ -113,8 +113,8 @@ namespace raven_integration { //Get non existant //Should return status code 400, api error code 2200 and a first target in details of "id" - ApiResponse a = await Util.GetAsync("User/2q2", await Util.GetTokenAsync("manager", "l3tm3in")); - Util.ValidateBadModelStateResponse(a, "id"); + ApiResponse R = await Util.GetAsync("User/2q2", await Util.GetTokenAsync("manager", "l3tm3in")); + Util.ValidateBadModelStateResponse(R, "id"); } @@ -128,29 +128,29 @@ namespace raven_integration [Fact] public async void PutConcurrencyViolationShouldFail() { + //CREATE + dynamic D = new JObject(); + D.name = Util.Uniquify("PutConcurrencyViolationShouldFail"); + D.ownerId = 1L; + D.active = true; + D.login = Util.Uniquify("LOGIN"); + D.password = Util.Uniquify("PASSWORD"); + D.roles = 0;//norole + D.localeId = 1;//random locale + D.userType = 3;//non scheduleable - //CREATE - - dynamic w2 = new JObject(); - w2.name = Util.Uniquify("PutConcurrencyViolationShouldFail"); - w2.dollarAmount = 2.22m; - w2.active = true; - w2.roles = 0; - - ApiResponse r2 = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), w2.ToString()); - Util.ValidateDataReturnResponseOk(r2); - long w2Id = r2.ObjectResponse["result"]["id"].Value(); - uint OriginalConcurrencyToken = r2.ObjectResponse["result"]["concurrencyToken"].Value(); - + ApiResponse R = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString()); + Util.ValidateDataReturnResponseOk(R); + long D1Id = R.ObjectResponse["result"]["id"].Value(); + uint OriginalConcurrencyToken = R.ObjectResponse["result"]["concurrencyToken"].Value(); //UPDATE //PUT - w2.name = Util.Uniquify("PutConcurrencyViolationShouldFail UPDATE VIA PUT "); - w2.OwnerId = 1; - w2.concurrencyToken = OriginalConcurrencyToken - 1;//bad token - ApiResponse PUTTestResponse = await Util.PutAsync("User/" + w2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), w2.ToString()); + D.name = Util.Uniquify("PutConcurrencyViolationShouldFail UPDATE VIA PUT "); + D.concurrencyToken = OriginalConcurrencyToken - 1;//bad token + ApiResponse PUTTestResponse = await Util.PutAsync("User/" + D1Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString()); Util.ValidateConcurrencyError(PUTTestResponse); @@ -165,19 +165,21 @@ namespace raven_integration [Fact] public async void PatchConcurrencyViolationShouldFail() { - //CREATE + dynamic D = new JObject(); + D.name = Util.Uniquify("PatchConcurrencyViolationShouldFail"); + D.ownerId = 1L; + D.active = true; + D.login = Util.Uniquify("LOGIN"); + D.password = Util.Uniquify("PASSWORD"); + D.roles = 0;//norole + D.localeId = 1;//random locale + D.userType = 3;//non scheduleable - dynamic w2 = new JObject(); - w2.name = Util.Uniquify("PatchConcurrencyViolationShouldFail"); - w2.dollarAmount = 2.22m; - w2.active = true; - w2.roles = 0; - - ApiResponse r2 = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), w2.ToString()); - Util.ValidateDataReturnResponseOk(r2); - long w2Id = r2.ObjectResponse["result"]["id"].Value(); - uint OriginalConcurrencyToken = r2.ObjectResponse["result"]["concurrencyToken"].Value(); + ApiResponse R = await Util.PostAsync("User", await Util.GetTokenAsync("manager", "l3tm3in"), D.ToString()); + Util.ValidateDataReturnResponseOk(R); + long w2Id = R.ObjectResponse["result"]["id"].Value(); + uint OriginalConcurrencyToken = R.ObjectResponse["result"]["concurrencyToken"].Value(); //PATCH