/// <summary> /// Returns a secured version of the list of entity IDs. /// </summary> /// <remarks> /// The first instance of an actual security check will result in security being calculated for the entire graph. /// The applicable user is noted, and if a different user attempts to access, then an exception is thrown. /// Note: this does not affect the cacheability, as caching happens at BulkRequestResult, which is unsecured. /// </remarks> /// <param name="list">The entities to secure</param> /// <param name="securityOption"></param> /// <param name="relTypeAndDir">Optionally, the relationship along which the entities were reached. Use negative for reverse.</param> /// <returns>The list to which the user has read permission.</returns> internal IReadOnlyCollection <long> SecureList(IReadOnlyCollection <long> list, SecurityOption securityOption, long relTypeAndDir = 0) { // Apply security bypass context if (SecurityBypassContext.IsActive) { return(list); } // If this relationship implies security, then immediately return results if (relTypeAndDir != 0) { RelationshipInfo relationshipInfo; if (_unsecuredGraphData.BulkSqlQuery.Relationships.TryGetValue(relTypeAndDir, out relationshipInfo)) { if (relationshipInfo.ImpliesSecurity) { return(list); } } // else assert false } // Determine if we have pre-calculated security if (_canRead == null) { // Security check on entire graph excluding implicitly secured) // Results returned as a predicate that performs fast lookup _canRead = BulkRequestResultSecurityHelper.GetEntityReadability(_entityAccessControlService, _unsecuredGraphData); _securedForUser = RequestContext.GetContext( ).Identity.Id; } else { // Ensure we are still accessing as the same user. long currentUser = RequestContext.GetContext( ).Identity.Id; if (currentUser != _securedForUser) { throw new InvalidOperationException("An entity graph may only be accessed by a single user."); } } if (securityOption == SecurityOption.DemandAll) { if (list.Any(id => !_canRead(id))) { // Security access has already failed, but for consistency of code paths, let Demand throw the exception. var ids = list.Select(id => new EntityRef(id)).ToList(); _entityAccessControlService.Demand(ids, new [] { Permissions.Read }); throw new Exception("Assert false - Demand should have thrown PlatformSecurityException"); } return(list); } else { List <long> result = list.Where(id => _canRead(id)).ToList( ); return(result); } }
/// <summary> /// Sets up this instance. /// </summary> void IDataSource.Setup(IProcessingContext context) { if (RootEntities == null) { throw new InvalidOperationException("RootEntities is not set."); } // Perform demand on root entity(ies). if (DemandReadPermission) { EntityAccessControlService.Demand(RootEntities.Select(id => new EntityRef(id)).ToList( ), new[] { Permissions.Read }); } using (new TenantAdministratorContext(TenantId)) { _aliasFieldId = WellKnownAliases.CurrentTenant.Alias; _reverseAliasFieldId = WellKnownAliases.CurrentTenant.ReverseAlias; // Get the instance to be exported IEnumerable <IEntity> instances = EntityRepository.Get(RootEntities); ICollection <long> typeIds = instances.Select(inst => inst.TypeIds.FirstOrDefault( )).Distinct().ToList(); if (typeIds.Count == 0) { typeIds = new[] { WellKnownAliases.CurrentTenant.Resource } } ; // Generate a cloning request factory for loading the data CloneEntityMemberRequestFactory requestFactory = new CloneEntityMemberRequestFactory(EntityRepository); EntityMemberRequest memberRequest = requestFactory.CreateRequest(typeIds); EntityRequest entityRequest = new EntityRequest { Request = memberRequest, Entities = RootEntities.Select(id => new EntityRef(id)) }; // Load data, unsecured, via EntityInfoService cache _bulkResult = BulkResultCache.GetBulkResult(entityRequest); // Load all UpgradeIDs IEnumerable <long> allIds = _bulkResult.AllEntities.Keys .Union(_bulkResult.Relationships.Keys.Select(k => k.TypeId)) .Union(_bulkResult.FieldValues.Keys.Select(k => k.FieldId)); _idToUpgradeId = UpgradeIdProvider.GetUpgradeIds(allIds); IEnumerable <long> relTypes = _bulkResult.Relationships.Keys.Select(key => key.TypeId).Distinct( ); IEnumerable <Relationship> relationships = EntityRepository.Get <Relationship>(relTypes); _relationshipTypeCache = relationships.ToDictionary( rel => _idToUpgradeId[rel.Id], rel => new RelationshipTypeEntry { CloneAction = rel.CloneAction_Enum, ReverseCloneAction = rel.ReverseCloneAction_Enum, Alias = rel.Alias, ReverseAlias = rel.ReverseAlias }); LoadDocumentCaches(context); } // Read permissions - for reading internal entities if (DemandReadPermission) { _canRead = BulkRequestResultSecurityHelper.GetEntityReadability(Factory.EntityAccessControlService, _bulkResult); } else { _canRead = id => true; } } /// <summary> /// Loads the application metadata. /// </summary> /// <param name="context">The context.</param> /// <returns></returns> /// <exception cref="System.InvalidOperationException">@Invalid package Id</exception> Metadata IDataSource.GetMetadata(IProcessingContext context) { List <Guid> roots = new List <Guid>( ); foreach (long rootId in RootEntities) { Guid rootGuid; if (_idToUpgradeId.TryGetValue(rootId, out rootGuid)) { roots.Add(rootGuid); } } var metadata = new Metadata { AppName = "Exported data", AppVerId = Guid.Empty, AppId = Guid.Empty, Description = "Exported data", Name = "Exported data", Version = "1.0", RootEntities = roots, //Dependencies = solutionDependencies, Type = SourceType.DataExport, PlatformVersion = SystemInfo.PlatformVersion, RelationshipTypeCallback = GetRelationshipMetadata }; return(metadata); }