public void Test_Demand_Failure() { EntityAccessControlService entityAccessControlService; Mock <IEntityAccessControlChecker> entityAccessControlChecker; EntityRef[] entities; EntityRef[] permissions; entities = new EntityRef[] { 1 }; permissions = new[] { Permissions.Read }; entityAccessControlChecker = new Mock <IEntityAccessControlChecker>(MockBehavior.Strict); entityAccessControlChecker.Setup(eacc => eacc.CheckAccess( It.Is <IList <EntityRef> >(e => e.SequenceEqual(entities, EntityRefComparer.Instance)), It.Is <IList <EntityRef> >(p => p.SequenceEqual(permissions, EntityRefComparer.Instance)), It.IsAny <EntityRef>())) .Returns(new Dictionary <long, bool>() { { entities[0].Id, false } }); entityAccessControlService = new EntityAccessControlService(entityAccessControlChecker.Object); Assert.That(() => entityAccessControlService.Demand(entities, permissions), Throws.TypeOf <PlatformSecurityException>()); entityAccessControlChecker.VerifyAll(); }
public void Test_CanCreateEntityTypes() { EntityAccessControlService entityAccessControlService; Mock <IEntityAccessControlChecker> entityAccessControlChecker; EntityType entityType; entityType = Entity.Get <EntityType>("core:person"); Assert.That(entityType, Is.Not.Null, "Person type not found"); entityAccessControlChecker = new Mock <IEntityAccessControlChecker>(MockBehavior.Strict); entityAccessControlChecker.Setup(eacc => eacc.CheckTypeAccess( It.Is <IList <EntityType> >(ets => ets.SequenceEqual(new [] { entityType }, new EntityEqualityComparer())), It.Is <EntityRef>(perm => perm.Id == Permissions.Create.Id), It.IsAny <EntityRef>())) .Returns <IList <EntityType>, EntityRef, EntityRef>( (ets, perm, user) => new Dictionary <long, bool> { { entityType.Id, true } }); entityAccessControlService = new EntityAccessControlService(entityAccessControlChecker.Object); Assert.That(entityAccessControlService.CanCreate(new [] { entityType }), Is.EquivalentTo(new [] { new KeyValuePair <long, bool>(entityType.Id, true) })); entityAccessControlChecker.VerifyAll(); }
public void Test_Demand_Success() { EntityAccessControlService entityAccessControlService; Mock <IEntityAccessControlChecker> entityAccessControlChecker; EntityRef[] entities; EntityRef[] permissions; using (DatabaseContext.GetContext(true)) { entities = new EntityRef[] { 1 }; permissions = new[] { Permissions.Read }; entityAccessControlChecker = new Mock <IEntityAccessControlChecker>(MockBehavior.Strict); entityAccessControlChecker.Setup(eacc => eacc.CheckAccess( It.Is <IList <EntityRef> >(e => e.SequenceEqual(entities, EntityRefComparer.Instance)), It.Is <IList <EntityRef> >(p => p.SequenceEqual(permissions, EntityRefComparer.Instance)), It.IsAny <EntityRef>())) .Returns(new Dictionary <long, bool>() { { entities[0].Id, true } }); entityAccessControlService = new EntityAccessControlService(entityAccessControlChecker.Object); Assert.That(() => entityAccessControlService.Demand(entities, permissions), Throws.Nothing); entityAccessControlChecker.VerifyAll(); } }
public void Test_Check_MultipleEntities() { EntityAccessControlService entityAccessControlService; Mock <IEntityAccessControlChecker> entityAccessControlChecker; EntityRef[] entities; EntityRef[] permissions; IDictionary <long, bool> result; using (DatabaseContext.GetContext(true)) { entities = new EntityRef[] { 1 }; permissions = new[] { Permissions.Read }; entityAccessControlChecker = new Mock <IEntityAccessControlChecker>(MockBehavior.Strict); entityAccessControlChecker.Setup(eacc => eacc.CheckAccess( It.Is <IList <EntityRef> >(e => e.SequenceEqual(entities, EntityRefComparer.Instance)), It.Is <IList <EntityRef> >(p => p.SequenceEqual(permissions, EntityRefComparer.Instance)), It.IsAny <EntityRef>())) .Returns(new Dictionary <long, bool>() { { entities[0].Id, true } }); entityAccessControlService = new EntityAccessControlService(entityAccessControlChecker.Object); result = entityAccessControlService.Check(entities, permissions); Assert.That(result, Has.Property("Count").EqualTo(1)); Assert.That(result, Has.Exactly(1).Property("Key").EqualTo(entities[0].Id).And.Property("Value").EqualTo(true)); entityAccessControlChecker.VerifyAll(); } }
public void Test_Check_SingleEntity() { EntityAccessControlService entityAccessControlService; Mock <IEntityAccessControlChecker> entityAccessControlChecker; Resource entity; EntityRef[] permissions; using (DatabaseContext.GetContext(true)) { entity = Entity.Create <Resource>(); permissions = new[] { Permissions.Read }; entityAccessControlChecker = new Mock <IEntityAccessControlChecker>(MockBehavior.Strict); entityAccessControlChecker.Setup(eacc => eacc.CheckAccess( It.Is <IList <EntityRef> >(e => e.SequenceEqual(new EntityRef[] { entity }, EntityRefComparer.Instance)), It.Is <IList <EntityRef> >(p => p.SequenceEqual(permissions, EntityRefComparer.Instance)), It.IsAny <EntityRef>())) .Returns(new Dictionary <long, bool>() { { entity.Id, true } }); entityAccessControlService = new EntityAccessControlService(entityAccessControlChecker.Object); Assert.That(entityAccessControlService.Check(entity, permissions), Is.True); entityAccessControlChecker.VerifyAll(); } }
/// <summary> /// Runs the report specified by ID. /// </summary> /// <param name="reportId">The report unique identifier.</param> /// <param name="settings">The settings for the report to be run.</param> /// <returns>ReportResult.</returns> /// <exception cref="System.ArgumentException"> /// The report identifier resource is not a report. /// </exception> /// <exception cref="PlatformSecurityException"> /// The user lacks read access to <paramref name="reportId"/>. /// </exception> public ReportCompletionData PrepareReport(long reportId, ReportSettings settings) { using (Profiler.Measure("ReportingInterface.PrepareReport {0}", reportId)) using (CacheContext cacheContext = new CacheContext( )) { // Check user has permission to report EntityAccessControlService.Demand(new[] { new EntityRef(reportId) }, new[] { Permissions.Read }); cacheContext.Entities.Add(reportId); using (CacheManager.ExpectCacheHits(true)) using (new SecurityBypassContext()) { // Validate the report entity Identifier Model.Report report; using (Profiler.Measure("GraphEntityRepository.Get", reportId)) { // Get the report from the graph database report = GraphEntityRepository.Get <Model.Report>(reportId, ReportHelpers.ReportPreloaderQuery); if (report == null) { throw new WebArgumentException("reportId"); } } settings = settings ?? new ReportSettings( ); settings.UseStructuredQueryCache = true; return(PrepareReport(report, settings, true)); } } }
public void Test_TraceCacheInvalidations_CanCreate(SecurityTraceLevel traceLevel) { EntityType entityType; EntityAccessControlService entityAccessControlService; UserAccount userAccount; int expectedOccurrences; userAccount = new UserAccount { Name = "Test User " + Guid.NewGuid() }; userAccount.Save(); entityType = new EntityType(); entityType.Save(); entityAccessControlService = new EntityAccessControlService( new EntityAccessControlChecker(), () => (int)traceLevel); using (new SetUser(userAccount)) { entityAccessControlService.CanCreate(entityType); } expectedOccurrences = -1; switch (traceLevel) { case SecurityTraceLevel.DenyVerbose: case SecurityTraceLevel.DenyBasic: case SecurityTraceLevel.AllVerbose: case SecurityTraceLevel.AllBasic: expectedOccurrences = 1; break; case SecurityTraceLevel.None: expectedOccurrences = 0; break; default: Assert.Fail("Unknown security trace level."); break; } IList <LogActivityLogEntry> activityLogEntries; activityLogEntries = Entity.GetInstancesOfType <LogActivityLogEntry>().ToList(); Assert.That(activityLogEntries, Has.Exactly(expectedOccurrences) .Property("LogEntrySeverity_Enum").EqualTo(LogSeverityEnum_Enumeration.InformationSeverity).And .Property("LogEventTime").EqualTo(DateTime.UtcNow).Within(TimeSpan.FromSeconds(10)).And .Property("Description").StartsWith( string.Format( "Access control check: Does user '{0}' have '{1}' access to entity(ies) '{2}'", userAccount.Name, Permissions.Create.Alias, entityType.Id))); }
public void Test_GetBehavior_Null() { EntityAccessControlService entityAccessControlService; entityAccessControlService = new EntityAccessControlService( new EntityAccessControlChecker(), () => (int)SecurityTraceLevel.None); Assert.That( () => entityAccessControlService.GetBehavior(null), Throws.TypeOf <ArgumentNullException>().And.Property("ParamName").EqualTo("entityIds")); }
public void Test_GetBehavior_Empty() { EntityAccessControlService entityAccessControlService; entityAccessControlService = new EntityAccessControlService( new EntityAccessControlChecker(), () => (int)SecurityTraceLevel.None); Assert.That( () => entityAccessControlService.GetBehavior(new long[0]), Is.EqualTo(MessageContextBehavior.New)); }
public void Test_GetBehavior_RequestContextNotSet() { EntityAccessControlService entityAccessControlService; entityAccessControlService = new EntityAccessControlService( new EntityAccessControlChecker(), () => (int)SecurityTraceLevel.None); Assert.That( () => entityAccessControlService.GetBehavior(new long[0]), Throws.InvalidOperationException); }
public void Test_GetBehavior_ForceTrace_Overlap() { EntityAccessControlService entityAccessControlService; entityAccessControlService = new EntityAccessControlService( new EntityAccessControlChecker(), () => (int)SecurityTraceLevel.None); using (new ForceSecurityTraceContext(1)) { Assert.That( () => entityAccessControlService.GetBehavior(new long[] { 1 }), Is.EqualTo(MessageContextBehavior.New | MessageContextBehavior.Capturing)); } }
public void Test_Creation() { EntityAccessControlService entityAccessControlService; IEntityAccessControlChecker entityAccessControlChecker; entityAccessControlChecker = new EntityAccessControlChecker(); entityAccessControlService = new EntityAccessControlService(entityAccessControlChecker); Assert.That(entityAccessControlService, Has.Property("EntityAccessControlChecker").EqualTo(entityAccessControlChecker)); Assert.That(entityAccessControlService, Has.Property("TraceLevelFactory").EqualTo((Func <int>)entityAccessControlService.GetTraceLevelSetting)); Assert.That(entityAccessControlService, Has.Property("TraceLevel").Not.Null); }
public void Test_GetBehavior_ForceTrace_NoOverlap() { EntityAccessControlService entityAccessControlService; const long entityId = 1; entityAccessControlService = new EntityAccessControlService( new EntityAccessControlChecker(), () => (int)SecurityTraceLevel.None); using (new ForceSecurityTraceContext(entityId)) { Assert.That( () => entityAccessControlService.GetBehavior(new long[] { entityId + 1 }), Is.EqualTo(MessageContextBehavior.New)); } }
/// <summary> /// Verify that the current user has permission to perform an import. /// </summary> /// <remarks> /// Loads all entities that are specified in the data source, then looks up the current user/tenant to see /// if those entities are present. Performs a security demand on those that are present. /// </remarks> /// <param name="source">Data source to read entities from.</param> /// <param name="permissions">Permission(s) to demand.</param> /// <param name="context">Processing context for reading the source.</param> /// <exception cref="PlatformSecurityException">Thrown if permission is denied.</exception> public void CheckEntityPermissions(IDataSource source, IList <EntityRef> permissions, IProcessingContext context) { // Load entities that would be imported IEnumerable <Guid> upgradeIds = source.GetEntities(context) .Select(e => e.EntityId).Distinct( ); // Note: UpgradeIdProvider will drop any upgradeIds that don't exist in the tenant. IList <EntityRef> entityIds = UpgradeIdProvider.GetIdsFromUpgradeIds(upgradeIds) .Select(pair => new EntityRef(pair.Value)).ToList( ); // Demand 'modify' for instances EntityAccessControlService.Demand(entityIds, permissions); }
public void Test_GetBehavior_Inspect_Overlap() { EntityAccessControlService entityAccessControlService; EventLogSettings eventLogSettings; IEntity testEntity; testEntity = Entity.Get <Resource>("core:name"); eventLogSettings = Entity.Get <EventLogSettings>(ForceSecurityTraceContext.EventLogSettingsAlias, true); eventLogSettings.InspectSecurityChecksOnResource.Add(Entity.Get <Resource>(testEntity.Id)); eventLogSettings.Save(); entityAccessControlService = new EntityAccessControlService( new EntityAccessControlChecker(), () => (int)SecurityTraceLevel.None); Assert.That( () => entityAccessControlService.GetBehavior(new [] { testEntity.Id }), Is.EqualTo(MessageContextBehavior.New | MessageContextBehavior.Capturing)); }
public void Test_GetBehavior_Inspect_NoOverlap() { EntityAccessControlService entityAccessControlService; EventLogSettings eventLogSettings; IEntity testEntity; testEntity = Entity.Get <Resource>("core:name"); eventLogSettings = Entity.Get <EventLogSettings>(ForceSecurityTraceContext.EventLogSettingsAlias, true); eventLogSettings.InspectSecurityChecksOnResource.Add(Entity.Get <Resource>("core:name")); eventLogSettings.Save(); Thread.Sleep(new TimeSpan(ForceSecurityTraceContext.TicksToWaitBeforeRefreshing)); entityAccessControlService = new EntityAccessControlService( new EntityAccessControlChecker(), () => (int)SecurityTraceLevel.None); Assert.That( () => entityAccessControlService.GetBehavior(new [] { testEntity.Id + 1 }), Is.EqualTo(MessageContextBehavior.New)); }
public void Test_GetTraceCacheInvalidationSetting() { int oldSetting; int newSetting; EntityAccessControlService entityAccessControlService; oldSetting = ConfigurationSettings.GetServerConfigurationSection().Security.Trace; try { newSetting = oldSetting + 1; ConfigurationSettings.GetServerConfigurationSection().Security.Trace = newSetting; entityAccessControlService = new EntityAccessControlService(); Assert.That(entityAccessControlService.GetTraceLevelSetting(), Is.EqualTo(newSetting)); } finally { ConfigurationSettings.GetServerConfigurationSection().Security.Trace = oldSetting; } }
/// <summary> /// Verify that the current user has permission to create instances of types mentioned in a data source. /// </summary> /// <remarks> /// Loads all relationship instances and searches for 'isOfType' relationships. /// Then performs a 'can create' check on those types, if they are present in the current tenant. /// </remarks> /// <param name="source">Data source to read relationships from.</param> /// <param name="context">Processing context for reading the source.</param> /// <exception cref="PlatformSecurityException">Thrown if permission is denied.</exception> public void CheckTypeCreatePermissions(IDataSource source, IProcessingContext context) { // Load types that would be imported IEnumerable <Guid> typeUpgradeIds = source.GetRelationships(context) .Where(rel => rel.TypeId == Helpers.IsOfTypeRelationshipUpgradeId) .Select(rel => rel.ToId).Distinct( ); // Note: UpgradeIdProvider will drop any upgradeIds that don't exist in the tenant. IEnumerable <long> typeIds = UpgradeIdProvider.GetIdsFromUpgradeIds(typeUpgradeIds) .Select(pair => pair.Value); IDictionary <long, EntityType> types = EntityRepository.Get <EntityType>(typeIds).ToDictionary(e => e.Id); // Demand 'create' for types IDictionary <long, bool> canCreate = EntityAccessControlService.CanCreate(types.Values.ToList( )); IList <long> denied = canCreate.Where(pair => !pair.Value).Select(pair => pair.Key).ToList( ); if (denied.Count > 0) { string message = "Permission denied to create records of type: " + string.Join(", ", denied.Select(id => types[id].Name)); throw new PlatformSecurityException(message); } }
/// <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); }
/// <summary> /// Traverses the identifiers. /// </summary> /// <returns> /// True if the next identifier was found; False otherwise. /// </returns> private bool TraverseIdentifiers( ) { ///// // Single bulk security check ///// if (_identifiersEnumerator == null) { if (!SecurityBypassContext.IsActive) { IList <EntityRef> permissions = new List <EntityRef>( ); permissions.Add(Permissions.Read); if (IsWriteable) { permissions.Add(Permissions.Modify); } IList <EntityRef> identifiers = _identifiers as IList <EntityRef> ?? _identifiers.ToList( ); if (_securityOption == SecurityOption.SkipDenied) { IDictionary <long, bool> accessControlCheckResult = EntityAccessControlService.Check(identifiers, permissions); _identifiersEnumerator = _identifiers.Where(er => accessControlCheckResult[er.Id]).GetEnumerator( ); } else if (_securityOption == SecurityOption.DemandAll) { EntityAccessControlService.Demand(identifiers, permissions); _identifiersEnumerator = _identifiers.GetEnumerator( ); } else { throw new ArgumentException(string.Format("Unknown SecurityOption: {0}", _securityOption)); } } else { _identifiersEnumerator = _identifiers.GetEnumerator( ); } } while (_identifiersEnumerator.MoveNext( )) { ///// // Get the current identifier. ///// EntityRef entityRef = _identifiersEnumerator.Current; IEntity entity = RetrieveEntity(entityRef); if (entity != null && entity.TenantId == RequestContext.TenantId) { ///// // Ensure the fields are loaded for this entity. ///// _bulkFieldLoadAction(entity); if ((_writable && !entity.IsReadOnly) || (!_writable && entity.IsReadOnly)) { ///// // Use this instance. ///// CurrentValue = entity; return(true); } ///// // Get a writable instance. ///// CurrentValue = entity.AsWritable( ); return(true); } } Dispose( ); return(false); }