private async Task SaveResponse(PgPurchaseOrderResponse payload, Cost cost, SystemAdminUserIdentity adminUser) { var purchaseOrderData = await _customDataService .GetCustomData <PgPurchaseOrderResponse>(cost.LatestCostStageRevisionId.Value, CustomObjectDataKeys.PgPurchaseOrderResponse) ?? new PgPurchaseOrderResponse(); _mapper.Map(payload, purchaseOrderData); if (string.Compare(payload.ApprovalStatus, ApprovalStatuses.Rejected, StringComparison.OrdinalIgnoreCase) == 0) { //null requisition ID should be allowed here purchaseOrderData.Requisition = payload.Requisition; purchaseOrderData.ApprovalStatus = payload.ApprovalStatus; } await _customDataService.Save(cost.LatestCostStageRevisionId.Value, CustomObjectDataKeys.PgPurchaseOrderResponse, purchaseOrderData, adminUser); await UpdatePaymentDetails(cost.LatestCostStageRevisionId.Value, purchaseOrderData, adminUser); var logEntries = new List <IActivityLogEntry> { new PoCreated(cost.CostNumber, purchaseOrderData.PoNumber, adminUser), new GoodsReceiptAllocated(cost.CostNumber, purchaseOrderData.GrNumber, adminUser), new RequisitionNumber(cost.CostNumber, purchaseOrderData.Requisition, adminUser) }; await _activityLogService.LogRange(logEntries); }
public async Task <OperationResponse> UpdateUser(UserIdentity userIdentity, Guid id, UpdateUserModel updateUserModel, BuType buType) { var xUserId = userIdentity.Id; var dbUser = _efContext.CostUser .Include(cu => cu.Agency) .ThenInclude(a => a.GlobalAgencyRegion) .ThenInclude(gar => gar.GlobalAgency) .Include(a => a.UserBusinessRoles) .ThenInclude(ubr => ubr.BusinessRole) .Include(a => a.UserUserGroups) .ThenInclude(uug => uug.UserGroup) .SingleOrDefault(a => a.Id == id); var userPerformingAction = _efContext.CostUser .Include(cu => cu.Agency) .Include(a => a.UserBusinessRoles) .ThenInclude(ubr => ubr.BusinessRole) .ThenInclude(br => br.Role) .SingleOrDefault(a => a.Id == xUserId); var businessRoles = _efContext.BusinessRole.ToList(); if (dbUser == null) { return(new OperationResponse(false, "User doesn't exist.")); } var hasAccess = await _permissionService.CheckHasAccess(xUserId, id, AclActionType.Edit, "user"); if (!hasAccess) { hasAccess = userPerformingAction.Agency.Id == dbUser.Agency.Id && userPerformingAction.UserBusinessRoles.Any(ubr => ubr.BusinessRole.Key == Constants.BusinessRole.AgencyAdmin); } SetMissingObjectId(updateUserModel.AccessDetails, businessRoles, out var abstractType); if (updateUserModel.AccessDetails.Count > 0 && dbUser.UserBusinessRoles.Count > 0) { updateUserModel.AccessDetails = GetAllAccessDetails(updateUserModel.AccessDetails, dbUser.UserBusinessRoles); } //This is done because if you dont it will keep its reference and cause issues lower in this function var toBeRemoved = dbUser.UserBusinessRoles .Where(ubr => !updateUserModel.AccessDetails.Any(ad => ubr.BusinessRoleId == ad.BusinessRoleId && ubr.ObjectType == ad.ObjectType && (ubr.ObjectId == ad.ObjectId || ubr.ObjectId == ad.OriginalObjectId || ubr.Labels.Contains(ad.LabelName) ) || ad.ObjectType == core.Constants.AccessObjectType.Client && ubr.ObjectType == ad.ObjectType && ubr.BusinessRoleId == ad.BusinessRoleId ) ) .ToList(); var entries = new List <IActivityLogEntry>(); await _efContext.InTransactionAsync(async() => { await RemoveUserFromExistingCosts(toBeRemoved, dbUser); await UpdateBusinessRoleLabels(updateUserModel.AccessDetails, dbUser); var agenciesToUpdateInElastic = new List <AbstractType>(); //Add New Access Rules foreach (var accessDetail in updateUserModel.AccessDetails) { if (!hasAccess) { hasAccess = userPerformingAction.UserBusinessRoles.Any(ubr => new[] { Roles.PlatformOwner, Roles.ClientAdmin, Roles.AdstreamAdmin }.Contains(ubr.BusinessRole.Role.Name) && ubr.ObjectId == accessDetail.ObjectId && ubr.CostUserId == xUserId ); if (!hasAccess) { // TODO: show the user which rules have been skipped _logger.Warning( $"User {userPerformingAction.Email}|{userPerformingAction.Id} does not have access to alter roles for {dbUser.Email}|{dbUser.Id} in scope of {accessDetail.ObjectType}|{accessDetail.ObjectId}, skipping this rule!"); continue; } } var selectedBusinessRole = businessRoles.SingleOrDefault(a => a.Id == accessDetail.BusinessRoleId); if (selectedBusinessRole == null) { throw new Exception($"Couldn't find business role with id {accessDetail.BusinessRoleId}"); } if (dbUser.UserBusinessRoles.Any(userBusinessRole => userBusinessRole != null && userBusinessRole.BusinessRole.Id == accessDetail.BusinessRoleId && userBusinessRole.ObjectType == accessDetail.ObjectType && (string.IsNullOrEmpty(accessDetail.LabelName) || userBusinessRole.Labels.Contains(accessDetail.LabelName)))) { //Break out if user already has access. _logger.Information( $"User {dbUser.Id} already has access to {accessDetail.ObjectType} | {accessDetail.ObjectId} with BusinessRole {selectedBusinessRole.Key}|{selectedBusinessRole.Id}"); continue; } if (accessDetail.ObjectType == core.Constants.AccessObjectType.Smo || accessDetail.ObjectType == core.Constants.AccessObjectType.Region && selectedBusinessRole.Key != Constants.BusinessRole.RegionalAgencyUser) { _logger.Information($"User {dbUser.Email}|{dbUser.Id} will be given access at cost creation."); var userBusinessRole = dbUser.UserBusinessRoles.FirstOrDefault(ubr => ubr.ObjectType == accessDetail.ObjectType); if (userBusinessRole != null) { var labels = userBusinessRole.Labels.ToList(); labels.Add(accessDetail.LabelName); userBusinessRole.Labels = labels.Distinct().ToArray(); } else { userBusinessRole = new UserBusinessRole(xUserId) { BusinessRole = selectedBusinessRole, Labels = new[] { accessDetail.LabelName }, ObjectType = accessDetail.ObjectType }; dbUser.UserBusinessRoles.Add(userBusinessRole); entries.Add(new UserRoleAssigned(dbUser.Email, selectedBusinessRole.Key, userIdentity)); await AddUserToExistingCosts(dbUser, userBusinessRole); } continue; } if (accessDetail.ObjectType == core.Constants.AccessObjectType.Region && selectedBusinessRole.Key == Constants.BusinessRole.RegionalAgencyUser) { _logger.Information($"User {dbUser.Email}|{dbUser.Id} will be given access at cost creation."); var userBusinessRole = dbUser.UserBusinessRoles.FirstOrDefault(ubr => ubr.ObjectType == accessDetail.ObjectType); if (userBusinessRole != null) { var labelIds = userBusinessRole.LocationIds.ToList(); labelIds.Add(accessDetail.LabelId.ToString()); userBusinessRole.LocationIds = labelIds.Distinct().ToArray(); var labels = userBusinessRole.Labels.ToList(); labels.Add(accessDetail.LabelName); userBusinessRole.Labels = labels.Distinct().ToArray(); } else { userBusinessRole = new UserBusinessRole(xUserId) { BusinessRole = selectedBusinessRole, Labels = new[] { accessDetail.LabelName }, LocationIds = new[] { accessDetail.LabelId?.ToString() }, ObjectType = accessDetail.ObjectType }; dbUser.UserBusinessRoles.Add(userBusinessRole); entries.Add(new UserRoleAssigned(dbUser.Email, selectedBusinessRole.Key, userIdentity)); await AddUserToExistingCosts(dbUser, userBusinessRole); } continue; } if (selectedBusinessRole.Key == Constants.BusinessRole.AdstreamAdmin || dbUser.Agency.Labels.Contains(Constants.Agency.PgOwnerLabel) || dbUser.Agency.Labels.Contains(Constants.Agency.AdstreamOwnerLabel) || (abstractType.Module != null && abstractType.Module?.ClientType == ClientType.Root)) { abstractType = await _efContext.AbstractType.FirstOrDefaultAsync(at => at.Id == accessDetail.ObjectId); var labels = abstractType.Module?.ClientType == ClientType.Root ? new[] { ClientType.Root.ToString() } : new[] { core.Constants.AccessObjectType.Client }; await GrantAccessToAbstractType(new [] { abstractType }, xUserId, buType, dbUser, selectedBusinessRole, accessDetail, labels); entries.Add(new UserRoleAssigned(dbUser.Email, selectedBusinessRole.Key, userIdentity)); agenciesToUpdateInElastic.Add(abstractType); } else if (selectedBusinessRole.Key == Constants.BusinessRole.CostConsultant) { // If business role is "Cost Consultant" the user doesn't get access to any object at this stage. dbUser.UserBusinessRoles.Add(new UserBusinessRole(xUserId) { BusinessRole = selectedBusinessRole, ObjectType = accessDetail.ObjectType }); entries.Add(new UserRoleAssigned(dbUser.Email, selectedBusinessRole.Key, userIdentity)); } else { var agencyToCreatePseudoAgencies = new[] { dbUser.Agency }; if (selectedBusinessRole.Key == Constants.BusinessRole.RegionalAgencyUser) { // TODO: use id of GlobalAgencyRegion in accessDetail rather then label because different Agencies can have regions with the same name. The same for Budget region. agencyToCreatePseudoAgencies = await _efContext.GlobalAgencyRegion .Where(gar => gar.Region == accessDetail.LabelName && gar.GlobalAgencyId == dbUser.Agency.GlobalAgencyRegion.GlobalAgencyId) .SelectMany(gar => gar.GlobalAgency.GlobalAgencyRegions) .SelectMany(gar => gar.Agencies) .ToArrayAsync(); } var pseudoAgencies = await _pgAgencyService.GetOrCreatePseudoAgencies(agencyToCreatePseudoAgencies); await GrantAccessToAbstractType(pseudoAgencies, xUserId, buType, dbUser, selectedBusinessRole, accessDetail); agenciesToUpdateInElastic.AddRange(pseudoAgencies); } } dbUser.ApprovalLimit = updateUserModel.ApprovalLimit; dbUser.EmailUrl = updateUserModel.EmailOverride; dbUser.NotificationBudgetRegionId = updateUserModel.NotificationBudgetRegionId; dbUser.UserGroups = await _permissionService.GetObjectUserGroups(dbUser.Id, null); await _activityLogService.LogRange(entries); _eventService.Add(new CostUsersUpdated(_mapper.Map <CostUserSearchItem[]>(new [] { dbUser }))); if (agenciesToUpdateInElastic.Any()) { _eventService.Add(new AgenciesUpdated(_mapper.Map <AgencySearchItem[]>(agenciesToUpdateInElastic))); } }, async() => await _eventService.SendAllPendingAsync()); return(new OperationResponse(true, "User updated.")); }