diff --git a/server/AyaNova/Controllers/UserController.cs b/server/AyaNova/Controllers/UserController.cs index df004085..586696dd 100644 --- a/server/AyaNova/Controllers/UserController.cs +++ b/server/AyaNova/Controllers/UserController.cs @@ -77,13 +77,11 @@ namespace AyaNova.Api.Controllers var o = await biz.GetAsync(id); if (o == null) return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND)); - - bool IsOutsideUser = (o.UserType == UserType.Customer || o.UserType == UserType.HeadOffice); - - if (IsOutsideUser && !AllowedOutsideUser) + + if (o.IsOutsideUser && !AllowedOutsideUser) return StatusCode(403, new ApiNotAuthorizedResponse()); - if (!IsOutsideUser && !AllowedInsideUser) + if (!o.IsOutsideUser && !AllowedInsideUser) return StatusCode(403, new ApiNotAuthorizedResponse()); return Ok(ApiOkResponse.Response(o)); diff --git a/server/AyaNova/biz/UserBiz.cs b/server/AyaNova/biz/UserBiz.cs index 7025c6cd..d5a42342 100644 --- a/server/AyaNova/biz/UserBiz.cs +++ b/server/AyaNova/biz/UserBiz.cs @@ -115,18 +115,28 @@ namespace AyaNova.Biz //CREATE internal async Task CreateAsync(User newObject) { + + //Also used for Contacts (customer type user or ho type user) + //by users with no User right but with Customer rights so need to double check here + if ( + (newObject.IsOutsideUser && !Authorized.HasCreateRole(CurrentUserRoles, AyaType.Customer)) || + (!newObject.IsOutsideUser && !Authorized.HasCreateRole(CurrentUserRoles, AyaType.User)) + ) + { + AddError(ApiErrorCode.NOT_AUTHORIZED); + return null; + } + + //password and login are optional but in the sense that they can be left out in a PUT // but if left out here we need to generate a random value instead so they can't login but the code is happy //because a login name and password are required always if (string.IsNullOrWhiteSpace(newObject.Password)) - { newObject.Password = Hasher.GenerateSalt();//set it to some big random value - } if (string.IsNullOrWhiteSpace(newObject.Login)) - { newObject.Login = Hasher.GenerateSalt();//set it to some big random value - } + //This is a new user so it will have been posted with a password in plaintext which needs to be salted and hashed newObject.Salt = Hasher.GenerateSalt(); @@ -173,6 +183,18 @@ namespace AyaNova.Biz AddError(ApiErrorCode.NOT_FOUND, "id"); return null; } + + //Also used for Contacts (customer type user or ho type user) + //by users with no User right but with Customer rights so need to double check here + if ( + (dbObject.IsOutsideUser && !Authorized.HasCreateRole(CurrentUserRoles, AyaType.Customer)) || + (!dbObject.IsOutsideUser && !Authorized.HasCreateRole(CurrentUserRoles, AyaType.User)) + ) + { + AddError(ApiErrorCode.NOT_AUTHORIZED); + return null; + } + User newObject = new User(); CopyObject.Copy(dbObject, newObject, "Id, Salt, Login, Password, CurrentAuthToken, DlKey, DlKeyExpire, Wiki, Serial"); string newUniqueName = string.Empty; @@ -236,6 +258,18 @@ namespace AyaNova.Biz AddError(ApiErrorCode.NOT_FOUND, "id"); return null; } + //Also used for Contacts (customer type user or ho type user) + //by users with no User right but with Customer rights so need to double check here + if ( + (dbObject.IsOutsideUser && !Authorized.HasModifyRole(CurrentUserRoles, AyaType.Customer)) || + (!dbObject.IsOutsideUser && !Authorized.HasModifyRole(CurrentUserRoles, AyaType.User)) + ) + { + AddError(ApiErrorCode.NOT_AUTHORIZED); + return null; + } + + User SnapshotOfOriginalDBObj = new User(); CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj); CopyObject.Copy(putObject, dbObject, "Id, Salt, CurrentAuthToken, LoginKey, DlKey, DlKeyExpire"); @@ -398,12 +432,25 @@ namespace AyaNova.Biz internal async Task DeleteAsync(long id) { - using (var transaction = await ct.Database.BeginTransactionAsync()) { try { User dbObject = await ct.User.SingleOrDefaultAsync(z => z.Id == id); + if (dbObject == null) + return false; + + //Also used for Contacts (customer type user or ho type user) + //by users with no User right but with Customer rights so need to double check here + if ( + (dbObject.IsOutsideUser && !Authorized.HasDeleteRole(CurrentUserRoles, AyaType.Customer)) || + (!dbObject.IsOutsideUser && !Authorized.HasDeleteRole(CurrentUserRoles, AyaType.User)) + ) + { + AddError(ApiErrorCode.NOT_AUTHORIZED); + return false; + } + await ValidateCanDelete(dbObject); if (HasErrors) return false; @@ -448,12 +495,25 @@ namespace AyaNova.Biz private async Task ValidateAsync(User proposedObj, User currentObj) { //skip validation if seeding - if(ServerBootConfig.SEEDING) return; + if (ServerBootConfig.SEEDING) return; //run validation and biz rules bool isNew = currentObj == null; - + //UserType change has Inside / Outside role implications + //a user attempting to change a UserType between inside or outside status must have the correct rights + //to *BOTH* Customer and User since it's affecting both types + if (currentObj.IsOutsideUser != proposedObj.IsOutsideUser) + { + //only can change if have both rights + if ( + !Authorized.HasModifyRole(CurrentUserRoles, AyaType.User) || + !Authorized.HasModifyRole(CurrentUserRoles, AyaType.Customer) + ) + { + AddError(ApiErrorCode.NOT_AUTHORIZED, "UserType"); + } + } //do we need to check the license situation? if (proposedObj.IsTech && proposedObj.Active) diff --git a/server/AyaNova/models/User.cs b/server/AyaNova/models/User.cs index 232677da..88586a2f 100644 --- a/server/AyaNova/models/User.cs +++ b/server/AyaNova/models/User.cs @@ -35,6 +35,14 @@ namespace AyaNova.Models [NotMapped, JsonIgnore] public AyaType AyaType { get => AyaType.User; } + public bool IsOutsideUser + { + get + { + return this.UserType == UserType.Customer || this.UserType == UserType.HeadOffice; + } + } + // [JsonIgnore]//hide from being returned (as null anyway) with User object in routes // public UserOptions UserOptions { get; set; } }//eoc @@ -114,6 +122,15 @@ namespace AyaNova.Models } } + public bool IsOutsideUser + { + get + { + return this.UserType == UserType.Customer || this.UserType == UserType.HeadOffice; + } + } + + [NotMapped, JsonIgnore] public AyaType AyaType { get => AyaType.User; }