public async Task <GrantAccessResponse> GrantUserAccess <T>( Guid roleId, Guid objectId, CostUser costUser, BuType buType, Guid?xUserId, string labels = null, bool saveChanges = true) where T : Entity, IUserGroup { var dbUser = await _efContext.CostUser.FindAsync(costUser.Id); var existingUserGroup = _efContext.UserGroup.FirstOrDefault(a => a.ObjectId == objectId && a.RoleId == roleId); if (existingUserGroup == null) { existingUserGroup = new UserGroup { Id = Guid.NewGuid(), ObjectId = objectId, RoleId = roleId, ObjectType = typeof(T).Name.ToSnakeCase(), Label = labels }; existingUserGroup.SetName(); } _efContext.UserUserGroup.Add(new UserUserGroup { UserGroupId = existingUserGroup.Id, UserId = costUser.Id, UserGroup = existingUserGroup }); dbUser.UserGroups = new[] { existingUserGroup.Id.ToString() }; if (saveChanges) { await _efContext.SaveChangesAsync(); } return(new GrantAccessResponse { UserGroups = new[] { existingUserGroup.Id.ToString() }, New = true, UserGroup = existingUserGroup }); }
public async Task GetByObjectIdAndClient_whenExists_shouldReturnAbstractType() { // Arrange var objectId = Guid.NewGuid(); const BuType buType = BuType.Pg; const string moduleName = "name of client module"; var module = new Module { Id = Guid.NewGuid(), ClientType = (ClientType)buType, AbstractType = new AbstractType { Id = Guid.NewGuid(), ObjectId = objectId, Type = AbstractObjectType.Module.ToString() }, Name = moduleName }; var modules = new List <Module> { module }; _efContextMock.MockAsyncQueryable(modules.AsQueryable(), c => c.Module); var expected = new core.Models.AbstractTypes.Module { Id = module.AbstractType.Id }; _mapperMock.Setup(m => m.Map <core.Models.AbstractTypes.Module>(It.Is <Module>(em => em.Id == module.Id))).Returns(expected); // Act var result = await _sut.GetClientModule(buType); // Assert result.Should().NotBeNull(); result.Should().BeSameAs(expected); }
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.")); }
/// <summary> /// Returns the list of updated/created pseudo agencies /// </summary> /// <param name="agencyAbstractTypes"></param> /// <param name="xUserId"></param> /// <param name="buType"></param> /// <param name="dbUser"></param> /// <param name="selectedBusinessRole"></param> /// <param name="accessDetail"></param> /// <param name="labels"></param> /// <returns></returns> private async Task GrantAccessToAbstractType(IEnumerable <AbstractType> agencyAbstractTypes, Guid xUserId, BuType buType, CostUser dbUser, BusinessRole selectedBusinessRole, AccessDetail accessDetail, string[] labels = null) { foreach (var agencyAbstractType in agencyAbstractTypes) { await _permissionService.GrantUserAccess <AbstractType>(selectedBusinessRole.RoleId, agencyAbstractType.Id, dbUser, buType, null, null, false); dbUser.UserBusinessRoles.Add(new UserBusinessRole(xUserId) { BusinessRole = selectedBusinessRole, ObjectId = agencyAbstractType.Id, ObjectType = accessDetail.ObjectType, Labels = labels ?? new string[0] }); _logger.Information( $"User {xUserId} granted access for object {agencyAbstractType.Id} of type Parent with BusinessRole {selectedBusinessRole.Key}|{selectedBusinessRole.Id}"); } }