示例#1
0
        bool EvaluateForwardTriggers(IResourceTriggerFilterPolicyCache policyCache, IEntity entity, IEnumerable <long> changedFields, IEnumerable <long> changedRels, IEnumerable <long> changedRevRels, List <Action> postSaveActions)
        {
            using (Profiler.Measure("ResourceTriggerFilterEventTarget.EvaluateForwardTriggers"))
            {
                var typeId = entity.TypeIds.FirstOrDefault();

                if (typeId != 0)
                {
                    List <ResourceTriggerFilterDef> policies;
                    if (policyCache.TypePolicyMap.TryGetValue(typeId, out policies))
                    {
                        if (policies.Any())
                        {
                            // we match on type
                            var allChanges = changedFields.Union(changedRels).Union(changedRevRels);

                            //
                            // Check if our entity matches any of the fields and rels
                            foreach (var policy in policies)
                            {
                                if (policy == null)
                                {
                                    continue; // assert false
                                }
                                var watchedFields = policyCache.PolicyToFieldsMap[policy.Id];
                                if (!watchedFields.Any() || watchedFields.Intersect(allChanges).Any())
                                {
                                    var handler = policyCache.PolicyTypeHandlerMap[policy.TypeIds.First()];

                                    if (handler != null)
                                    {
                                        var isNew = entity.IsTemporaryId;

                                        bool result = false;
                                        SecurityBypassContext.RunAsUser(() =>
                                        {
                                            if (handler.OnBeforeSave(policy, entity, isNew, changedFields, changedRels, changedRevRels))
                                            {
                                                result = true;
                                            }
                                            else
                                            {
                                                AddWrappedPostSaveAction(postSaveActions, () => handler.OnAfterSave(policy, entity, isNew, changedFields, changedRels, changedRevRels));
                                            }
                                        });

                                        if (result)
                                        {
                                            return(true);        // We failed, so bug out
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(false);
        }
        /// <summary>
        /// Is there an access rule for the specified type(s) that includes the requested permission? E.g. create.
        /// </summary>
        /// <param name="entityType">
        /// The <see cref="EntityType"/> to check. This cannot be null.
        /// </param>
        /// <param name="permission">The permission being sought.</param>
        /// <param name="user"> The user requesting access. This cannot be null. </param>
        /// <returns>
        /// True if the user can create entities of that type, false if not.
        /// </returns>
        private bool CheckTypeAccess(EntityType entityType, EntityRef permission, EntityRef user)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }
            if (entityType == null)
            {
                throw new ArgumentNullException(nameof(entityType));
            }

            List <EntityType> entityTypesToCheck;
            bool result;

            using (new SecurityBypassContext())
                using (Profiler.MeasureAndSuppress("SecuresFlagEntityAccessControlChecker.CheckTypeAccess"))
                {
                    // Check if the type itself can be created
                    using (MessageContext messageContext = new MessageContext(EntityAccessControlService.MessageName))
                    {
                        messageContext.Append(() => "Checking access rules first:");

                        IDictionary <long, bool> resultAsDict     = null;
                        List <EntityType>        entityTypeAsList = new List <EntityType> {
                            entityType
                        };
                        SecurityBypassContext.RunAsUser(() =>
                                                        resultAsDict = Checker.CheckTypeAccess(entityTypeAsList, permission, user));

                        result = resultAsDict [entityType.Id];

                        if (result)
                        {
                            return(result);
                        }
                    }

                    // Walk graph to get list of types to check
                    entityTypesToCheck = ReachableTypes(entityType.ToEnumerable( ), true, false)
                                         .Select(walkStep => walkStep.Node)
                                         .Where(et => et != entityType) // skip root type.
                                         .ToList( );

                    result = CheckTypeAccessRelatedTypesImpl(entityType, permission, user, entityTypesToCheck);
                    if (!result)
                    {
                        return(false);
                    }

                    // Register cache invalidations
                    using (CacheContext cacheContext = CacheContext.GetContext( ))
                    {
                        cacheContext.EntityTypes.Add(WellKnownAliases.CurrentTenant.Relationship);
                        cacheContext.FieldTypes.Add(WellKnownAliases.CurrentTenant.SecuresFrom, WellKnownAliases.CurrentTenant.SecuresTo, WellKnownAliases.CurrentTenant.SecuresFromReadOnly, WellKnownAliases.CurrentTenant.SecuresToReadOnly);
                    }
                }

            return(result);
        }
示例#3
0
        public void Test_RunAsUser()
        {
            bool elevated;

            elevated = false;
            SecurityBypassContext.RunAsUser(() => elevated = SecurityBypassContext.IsActive);
            Assert.That(elevated, Is.False, "Elevated");
        }
示例#4
0
        /// <summary>
        /// Get the value of an access control field.
        /// </summary>
        /// <param name="entityId">The entityId</param>
        /// <param name="fieldId">The fieldId</param>
        /// <returns>A A typed value.</returns>
        internal static TypedValue TryGetAccessControlField(long entityId, IEntityRef fieldId)
        {
            var value = new TypedValue {
                Type = DatabaseType.BoolType
            };

            if (entityId < 1)
            {
                throw new ArgumentNullException("entityId");
            }

            if (fieldId == null || fieldId.Namespace != "core")
            {
                throw new ArgumentException("fieldId: invalid namespace");
            }

            switch (fieldId.Alias)
            {
            case "canCreateType":
            {
                var entityType = Entity.Get <EntityType>(entityId);

                if (entityType == null)
                {
                    throw new InvalidOperationException("Assert false: attempted to load an entity as an EntityType which is not a type.");
                }

                SecurityBypassContext.RunAsUser(() => {
                        value.Value = AccessControlService.CanCreate(entityType);
                    });
                break;
            }

            case "canModify":
            {
                SecurityBypassContext.RunAsUser(() => {
                        value.Value = AccessControlService.Check(entityId, new[] { Permissions.Modify });
                    });
                break;
            }

            case "canDelete":
            {
                SecurityBypassContext.RunAsUser(() => {
                        value.Value = AccessControlService.Check(entityId, new[] { Permissions.Delete });
                    });
                break;
            }

            default:
            {
                // return null value
                break;
            }
            }
            return(value);
        }
        /// <summary>
        /// Perform a create using the provided update action. To be used for testing.
        /// </summary>
        /// <param name="resType"></param>
        /// <param name="updateAction"></param>
        public static IEntity PerformCreate(EntityType resType, Action <IEntity> updateAction)
        {
            try
            {
                IEntity newEntity = null;

                SecurityBypassContext.RunAsUser(() =>
                {
                    newEntity = Entity.Create(resType);

                    var provider  = Factory.Current.Resolve <IEntityDefaultsDecoratorProvider>();
                    var decorator = provider.GetDefaultsDecorator(resType.Id);
                    // Do per-entity work
                    decorator.SetDefaultValues(newEntity.ToEnumerable());
                });

                updateAction(newEntity);


                SecurityBypassContext.RunAsUser(() =>
                {
                    newEntity.Save();
                });

                return(newEntity);
            }
            catch (ValidationException ex)
            {
                string message = ex.FieldName != null?string.Format("{0}: {1}", ex.FieldName, ex.Message) : ex.Message;

                throw new WorkflowRunException(message, ex);
            }
            catch (CardinalityViolationException ex)
            {
                throw new WorkflowRunException(ex.Message, ex);
            }
            catch (DuplicateKeyException ex)
            {
                throw new WorkflowRunException(ex.Message, ex);
            }
            catch (InvalidCastException ex)
            {
                throw new WorkflowRunException("Incorrect data type", ex);
            }
            catch (SqlException ex)
            {
                if (ex.Number == 547)
                {
                    throw new CreateInvalidReferenceException(ex);
                }
                else
                {
                    throw;
                }
            }
        }
示例#6
0
        public void Test_NoBypassContext()
        {
            bool elevated;

            elevated = false;
            SecurityBypassContext.Elevate(() => elevated = SecurityBypassContext.IsActive);
            Assert.That(elevated, Is.True, "Elevate is not elevated");
            SecurityBypassContext.RunAsUser(() => elevated = SecurityBypassContext.IsActive);
            Assert.That(elevated, Is.False, "RunAsUser is elevated");
        }
示例#7
0
        /// <summary>
        /// Update all the the provided expressions.
        /// </summary>
        private Dictionary <WfExpression, object> ResolveExpressions(IRunState context, ICollection <WfExpression> expressions)
        {
            Dictionary <WfExpression, object> result = null;

            // evaluate the expressions and update the targeted values
            SecurityBypassContext.RunAsUser(() =>
            {
                result = expressions.ToDictionary(e => e, e => EvaluateExpression(context, e));
            });

            return(result);
        }
示例#8
0
        bool OnBeforeRelsThisEnd(IEnumerable <IEntity> entities, List <Action> postSaveActions)
        {
            using (Profiler.Measure("ResourceTriggerFilterEventTarget.OnBeforeDelete OnBeforeRelsThisEnd"))
            {
                var _policy = Factory.ResourceTriggerFilterPolicyCache;

                foreach (var entity in entities)
                {
                    var typeId = entity.TypeIds.FirstOrDefault();

                    if (typeId != 0)
                    {
                        var policies = _policy.TypePolicyMap[typeId];
                        if (policies != null)
                        {
                            foreach (var policy in policies)
                            {
                                var handler = _policy.PolicyTypeHandlerMap[policy.TypeIds.First()];

                                if (handler != null)
                                {
                                    var isNew = entity.IsTemporaryId;

                                    bool result = false;
                                    SecurityBypassContext.RunAsUser(() =>
                                    {
                                        if (handler.OnBeforeDelete(policy, entity))
                                        {
                                            result = true;
                                        }
                                        else
                                        {
                                            postSaveActions.Add(() => handler.OnAfterDelete(policy, entity));
                                        }
                                    });

                                    if (result)
                                    {
                                        return(true);        // We failed, so bug out
                                    }
                                }
                            }
                        }
                    }
                }

                return(false);
            }
        }
示例#9
0
        void IRunNowActivity.OnRunNow(IRunState context, ActivityInputs inputs)
        {
            var ResourceToUpdateKey = GetArgumentKey("setChoiceActivityResourceArgument");
            var ValueKey            = GetArgumentKey("setChoiceActivityValueArgument");
            var FieldKey            = GetArgumentKey("setChoiceActivityFieldArgument");
            var replaceExistingKey  = GetArgumentKey("setChoiceActivityReplaceExisting");

            var choiceToUpdateRef = (IEntity)inputs[FieldKey];
            var resId             = (IEntity)inputs[ResourceToUpdateKey];
            var valueId           = (IEntity)inputs[ValueKey];

            bool replaceExisting = false;

            object replaceExistingObj;

            if (inputs.TryGetValue(replaceExistingKey, out replaceExistingObj))
            {
                replaceExisting = (bool?)replaceExistingObj ?? false;
            }

            var relationship = choiceToUpdateRef.As <Relationship>();

            SecurityBypassContext.RunAsUser(() =>
            {
                var resource = resId.AsWritable();    // Does not have to be a writeable copy because we are only manipulating relationships.

                var cardinality = relationship.Cardinality_Enum ?? CardinalityEnum_Enumeration.ManyToMany;

                replaceExisting =
                    replaceExisting
                    ||
                    cardinality == CardinalityEnum_Enumeration.OneToOne
                    ||
                    cardinality == CardinalityEnum_Enumeration.ManyToOne;


                var relCollection = resource.GetRelationships(choiceToUpdateRef, Direction.Forward);        // you can't have a reverse choice field.

                if (replaceExisting)
                {
                    relCollection.Clear();
                }

                relCollection.Add(valueId);

                resource.Save();
            });
        }
示例#10
0
        public void Test_OneBypassContext()
        {
            bool elevated;

            elevated = false;
            using (new SecurityBypassContext())
            {
                Assert.That(SecurityBypassContext.IsActive, Is.True, "Not active");

                SecurityBypassContext.Elevate(() => elevated = SecurityBypassContext.IsActive);
                Assert.That(elevated, Is.True, "Elevate is not elevated");

                SecurityBypassContext.RunAsUser(() => elevated = SecurityBypassContext.IsActive);
                Assert.That(elevated, Is.False, "RunAsUser is elevated");
            }
        }
示例#11
0
        void IRunNowActivity.OnRunNow(IRunState context, ActivityInputs inputs)
        {
            var typeRefKey   = GetArgumentKey("getResourcesResourceType");
            var reportRefKey = GetArgumentKey("getResourcesReport");
            var listKey      = GetArgumentKey("getResourcesList");
            var firstKey     = GetArgumentKey("getResourcesFirst");
            var countKey     = GetArgumentKey("getResourcesCount");

            object     o;
            EntityType resourceType = null;
            IEntity    reportRef    = null;

            if (inputs.TryGetValue(typeRefKey, out o))
            {
                if (o != null)
                {
                    resourceType = ((IEntity)o).As <EntityType>();
                }
            }

            if (inputs.TryGetValue(reportRefKey, out o))
            {
                if (o != null)
                {
                    reportRef = (IEntity)o;
                }
            }

            if (resourceType == null && reportRef == null)
            {
                throw new WorkflowRunException("Get Resources must have one of either the Type or Report parameters specified.");
            }


            IEnumerable <IEntity> list = null;

            SecurityBypassContext.RunAsUser(() =>
            {
                list = reportRef != null ? GetListFromReport(context, reportRef) : GetListFromType(resourceType);
            });

            context.SetArgValue(ActivityInstance, listKey, list);
            context.SetArgValue(ActivityInstance, firstKey, list.FirstOrDefault());
            context.SetArgValue(ActivityInstance, countKey, list.Count());
        }
        void IRunNowActivity.OnRunNow(IRunState context, ActivityInputs inputs)
        {
            var resourceToDeleteKey = GetArgumentKey("deleteActivityResourceArgument");

            var res = (IEntity)inputs[resourceToDeleteKey];

            if (res == null)
            {
                throw new WorkflowRunException_Internal("No record provided.", null);
            }

            SecurityBypassContext.RunAsUser(() =>
            {
                Entity.Delete(res.Id);
            });

            context.RemoveReference(res.Id);
        }
        private bool CheckTypeAccessRelatedTypesImpl(EntityType entityType, EntityRef permission, EntityRef user, List <EntityType> entityTypesToCheck)
        {
            bool result = false;

            using (MessageContext messageContext = new MessageContext(EntityAccessControlService.MessageName))
            {
                IDictionary <long, bool> canAccessTypes;
                string message;

                // Allow access if the user has access to any of the related types
                if (entityTypesToCheck.Count == 0)
                {
                    messageContext.Append(() => "No entity types found to check.");
                    return(false);
                }

                // Check whether the user has access to the given types
                canAccessTypes = null;
                SecurityBypassContext.RunAsUser(() =>
                                                canAccessTypes = Checker.CheckTypeAccess(
                                                    entityTypesToCheck.ToList( ),
                                                    permission,
                                                    user));

                message = string.Format(
                    "Checking security relationship(s) to see whether user can create entities of type '{0}': ",
                    entityType.Name ?? entityType.Id.ToString( ));

                if (canAccessTypes.Any(kvp => kvp.Value))
                {
                    messageContext.Append(() => string.Format(
                                              "{0} Allowed due to create access to entity type(s) '{1}'",
                                              message,
                                              canAccessTypes.Select(kvp => entityTypesToCheck.First(et => et.Id == kvp.Key).Name ?? kvp.Key.ToString( ))));

                    result = true;
                }
                else
                {
                    messageContext.Append(() => $"{message} Denied");
                }
            }
            return(result);
        }
        /// <summary>
        /// Perform a update using the provided update action. To be used for testing.
        /// </summary>
        /// <param name="entityToUpdate"></param>
        /// <param name="updateAction"></param>
        public static void PerformUpdate(IEntity entityToUpdate, Action <IEntity> updateAction)
        {
            try
            {
                updateAction(entityToUpdate);

                SecurityBypassContext.RunAsUser(() =>
                {
                    entityToUpdate.Save();
                });
            }
            catch (EDC.ReadiNow.Model.ValidationException ex)
            {
                string message = ex.FieldName != null?string.Format("{0}: {1}", ex.FieldName, ex.Message) : ex.Message;

                throw new WorkflowRunException(message, ex);
            }
            catch (CardinalityViolationException ex)
            {
                throw new WorkflowRunException(ex.Message, ex);
            }
            catch (DuplicateKeyException ex)
            {
                throw new WorkflowRunException(ex.Message, ex);
            }
            catch (InvalidCastException ex)
            {
                throw new WorkflowRunException("Incorrect data type", ex);
            }
            catch (SqlException ex)
            {
                if (ex.Number == 547)
                {
                    throw new UpdateInvalidReferenceException(ex);
                }
                else
                {
                    throw;
                }
            }
        }
示例#15
0
 public void Test_RunAsUser_NullAction()
 {
     Assert.That(() => SecurityBypassContext.RunAsUser(null),
                 Throws.TypeOf <ArgumentNullException>().And.Property("ParamName").EqualTo("action"));
 }
        /// <summary>
        /// Check whether the user has access to the entities by following relationships where the
        /// <see cref="Relationship"/> type has the <see cref="Relationship.SecuresTo"/> or
        /// <see cref="Relationship.SecuresFrom"/> flag set.
        /// </summary>
        /// <param name="permissions">
        /// The permissions to check for. This cannot be null or contain null.
        /// </param>
        /// <param name="user">
        /// The user to do the check access for. This cannot be null.
        /// </param>
        /// <param name="entitiesToCheck">
        /// The entities to check. This cannot be null or contain null.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// No argument can be null.
        /// </exception>
        internal ISet <EntityRef> CheckAccessControlByRelationship(IList <EntityRef> permissions, EntityRef user,
                                                                   ISet <EntityRef> entitiesToCheck)
        {
            if (permissions == null)
            {
                throw new ArgumentNullException("permissions");
            }
            if (permissions.Contains(null))
            {
                throw new ArgumentNullException("permissions", "Cannot contain null");
            }
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (entitiesToCheck == null)
            {
                throw new ArgumentNullException("entitiesToCheck");
            }

            EntityMemberRequest entityMemberRequest;
            IDictionary <long, ISet <EntityRef> > entityTypes;
            IEnumerable <EntityData> entitiesData;
            IDictionary <long, bool> accessToRelatedEntities;
            HashSet <EntityRef>      result;
            IList <long>             relatedAccessibleEntities;
            EntityType entityType;

            using (Profiler.MeasureAndSuppress("SecuresFlagEntityAccessControlChecker.CheckAccessControlByRelationship"))
            {
                result = new HashSet <EntityRef>(EntityRefComparer.Instance);

                entityTypes = TypeRepository.GetEntityTypes(entitiesToCheck);
                using (MessageContext outerMessageContext = new MessageContext(EntityAccessControlService.MessageName))
                    foreach (KeyValuePair <long, ISet <EntityRef> > entitiesType in entityTypes)
                    {
                        outerMessageContext.Append(() =>
                                                   string.Format(
                                                       "Checking relationships for entity(ies) \"{0}\" of type \"{1}\":",
                                                       string.Join(", ", entitiesType.Value.Select(er => string.Format("'{0}' ({1})", er.Entity.As <Resource>().Name, er.Id))),
                                                       string.Join(", ", string.Format("'{0}' ({1})", Entity.Get <Resource>(entitiesType.Key).Name, entitiesType.Key))));

                        using (MessageContext innerMessageContext = new MessageContext(EntityAccessControlService.MessageName))
                        {
                            entityType = Entity.Get <EntityType>(entitiesType.Key);
                            if (entityType != null)
                            {
                                entityMemberRequest = EntityMemberRequestFactory.BuildEntityMemberRequest(entityType, permissions);
                                if (entityMemberRequest.Relationships.Count > 0)
                                {
                                    IList <EntityRef> relatedEntitiesToCheck;

                                    innerMessageContext.Append(() => string.Format("Security relationship structure for entity type '{0}':", entityType.Id));
                                    TraceEntityMemberRequest(entityMemberRequest);

                                    // Get the IDs of entities to check security for
                                    EntityRequest request = new EntityRequest
                                    {
                                        Entities          = entitiesType.Value,
                                        Request           = entityMemberRequest,
                                        IgnoreResultCache = true    // security engine does its own result caching
                                    };
                                    entitiesData = BulkRequestRunner.GetEntitiesData(request).ToList( );

                                    // Do a single security check for all entities related to
                                    // the entities passed in, excluding the original entities
                                    // that failed the security check.
                                    relatedEntitiesToCheck = Delegates
                                                             .WalkGraph(
                                        entitiesData,
                                        entityData =>
                                        entityData.Relationships.SelectMany(relType => relType.Entities))
                                                             .Select(ed => ed.Id)
                                                             .Where(er => !entitiesType.Value.Contains(er, EntityRefComparer.Instance))
                                                             .ToList();
                                    if (relatedEntitiesToCheck.Count > 0)
                                    {
                                        // Add the relationship types to watch for cache invalidations
                                        using (CacheContext cacheContext = CacheContext.GetContext())
                                        {
                                            IList <long> relationshipTypes = Delegates
                                                                             .WalkGraph(entityMemberRequest.Relationships,
                                                                                        rr => rr.RequestedMembers.Relationships)
                                                                             .Select(rr => rr.RelationshipTypeId.Id)
                                                                             .ToList();

                                            cacheContext.RelationshipTypes.Add(relationshipTypes);
                                        }

                                        // ReSharper disable AccessToModifiedClosure
                                        // Do a single access check for all entities for efficiency, then pick the
                                        // important ones for each requested entity below.
                                        accessToRelatedEntities = null;
                                        innerMessageContext.Append(
                                            () => string.Format(
                                                "Checking related entities '{0}':",
                                                string.Join(", ", relatedEntitiesToCheck.Select(er => er.Id))));
                                        SecurityBypassContext.RunAsUser(
                                            () =>
                                            accessToRelatedEntities =
                                                Checker.CheckAccess(relatedEntitiesToCheck, permissions, user));
                                        // ReSharper restore AccessToModifiedClosure

                                        foreach (EntityData entityData in entitiesData)
                                        {
                                            // Get the related entities to check
                                            relatedEntitiesToCheck = Delegates.WalkGraph(
                                                entityData,
                                                ed => ed.Relationships.SelectMany(relType => relType.Entities))
                                                                     .Select(ed => ed.Id)
                                                                     .ToList();

                                            // Remove the start entity for the query, since security has
                                            // already been checked on it.
                                            relatedEntitiesToCheck.Remove(entityData.Id);

                                            // Get the related entities the user has access to
                                            relatedAccessibleEntities = accessToRelatedEntities
                                                                        .Where(kvp => kvp.Value && relatedEntitiesToCheck.Contains(kvp.Key, EntityRefComparer.Instance))
                                                                        .Select(kvp => kvp.Key)
                                                                        .ToList();

                                            // Grant access if the user has access to ANY of the related
                                            // entities.
                                            if (relatedEntitiesToCheck.Count > 0 &&
                                                relatedAccessibleEntities.Count > 0)
                                            {
                                                result.Add(entityData.Id);

                                                // ReSharper disable AccessToModifiedClosure
                                                innerMessageContext.Append(
                                                    () => string.Format(
                                                        "Access to '{0}' granted due to corresponding access to '{1}'",
                                                        string.Join(", ", relatedEntitiesToCheck.Select(id => string.Format("'{0}' ({1})", Entity.Get <Resource>(id).Name, id))),
                                                        string.Join(", ", relatedAccessibleEntities.Select(id => string.Format("'{0}' ({1})", Entity.Get <Resource>(id).Name, id)))));
                                                // ReSharper restore AccessToModifiedClosure
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    innerMessageContext.Append(() => string.Format("No relationships found to check for entity type '{0}'.", entityType.Id));
                                }
                            }
                            else
                            {
                                EventLog.Application.WriteWarning("Type ID {0} for entities '{1}' is not a type",
                                                                  entitiesType.Key, string.Join(", ", entitiesType.Value));
                            }
                        }
                    }
            }

            return(result);
        }
        void IRunNowActivity.OnRunNow(IRunState context, ActivityInputs inputs)
        {
            var OriginKey          = GetArgumentKey("setRelationshipActivityOriginArgument");
            var DestinationKey     = GetArgumentKey("setRelationshipActivityDestinationArgument");
            var RelationshipKey    = GetArgumentKey("setRelationshipActivityRelationshipArgument");
            var replaceExistingKey = GetArgumentKey("setRelationshipActivityReplaceExisting");
            var isReverseKey       = GetArgumentKey("setRelationshipActivityIsReverse");

            var originRef       = (IEntity)inputs[OriginKey];
            var relationshipRef = (IEntity)inputs[RelationshipKey];

            IEntity destinationRef = null;

            object destinationRefObj;

            if (inputs.TryGetValue(DestinationKey, out destinationRefObj))
            {
                destinationRef = (IEntity)destinationRefObj;
            }

            bool replaceExisting = false;

            object replaceExistingObj;

            if (inputs.TryGetValue(replaceExistingKey, out replaceExistingObj))
            {
                replaceExisting = (bool?)replaceExistingObj ?? false;
            }

            var direction = Direction.Forward;

            object isReverseObj;

            if (inputs.TryGetValue(isReverseKey, out isReverseObj))
            {
                direction = ((bool?)isReverseObj ?? false) ? Direction.Reverse : Direction.Forward;
            }


            var relationship = relationshipRef.As <Relationship>();

            SecurityBypassContext.RunAsUser(() =>
            {
                var origin = originRef.AsWritable <Entity>();

                var cardinality = relationship.Cardinality_Enum ?? CardinalityEnum_Enumeration.ManyToMany;

                replaceExisting =
                    replaceExisting
                    ||
                    cardinality == CardinalityEnum_Enumeration.OneToOne
                    ||
                    (direction == Direction.Forward && cardinality == CardinalityEnum_Enumeration.ManyToOne)
                    ||
                    (direction == Direction.Reverse && cardinality == CardinalityEnum_Enumeration.OneToMany)
                ;

                var relCollection = origin.GetRelationships(relationshipRef, direction);

                if (replaceExisting)
                {
                    relCollection.Clear();
                }

                if (destinationRef != null)
                {
                    relCollection.Add(destinationRef);
                }

                origin.Save();
            });
        }
        /// <summary>
        /// Update a workflow using a update action. This will take care of handling paused instances by cloning.
        /// </summary>
        /// <param name="workflowId"></param>
        /// <param name="updateAction"></param>
        /// <returns>True if the workflow needed to be clones</returns>
        public static bool Update(long workflowId, Action updateAction)
        {
            using (new SecurityBypassContext(true))
            {
                Workflow clone    = null;
                var      original = Entity.Get <Workflow>(workflowId);

                if (original == null)
                {
                    throw new WebArgumentException("The provided Workflow Id is not a workflow");
                }

                if (original.WfNewerVersion != null)
                {
                    throw new WebArgumentException("This Workflow is not the latest version so cannot be updated");
                }

                using (var databaseContext = DatabaseContext.GetContext(true))
                {
                    var havePausedInstances            = original.RunningInstances.Any(r => r.WorkflowRunStatus_Enum == WorkflowRunState_Enumeration.WorkflowRunPaused);
                    IDictionary <long, long> clonedIds = null;

                    if (havePausedInstances)
                    {
                        // clone the workflow (Need to save because the clone occurs in the DB)
                        clone     = original.Clone <Workflow>();
                        clonedIds = clone.Save();

                        // There seems to be a bug in entity clone that means you can't remove solution till after it has been saved.
                        clone            = clone.AsWritable <Workflow>();
                        clone.InSolution = null;

                        // Fix up the version linking - again we can't seem to do it as a single save.
                        var originalWfOlderVersionId = original.WfOlderVersion != null ? original.WfOlderVersion.Id : -1;

                        original = original.AsWritable <Workflow>();
                        original.WfOlderVersion = clone;
                        original.Save();

                        if (originalWfOlderVersionId != -1)
                        {
                            clone.WfOlderVersion = Entity.Get <Workflow>(originalWfOlderVersionId);
                        }

                        clone.Save();
                    }

                    SecurityBypassContext.RunAsUser(updateAction);

                    original = original.AsWritable <Workflow>();

                    original.WorkflowUpdateHash = _randGen.Next().ToString(CultureInfo.InvariantCulture);     // ideally this should be a hash of only the important aspects of the workflow
                    original.WorkflowVersion    = (original.WorkflowVersion ?? 1) + 1;

                    if (havePausedInstances)
                    {
                        original.WfOlderVersion = clone;
                        WorkflowRunHelper.MoveRuns(original, clone, clonedIds);
                    }

                    original.Save();    // This will also save the close

                    databaseContext.CommitTransaction();

                    return(havePausedInstances);
                }
            }
        }
示例#19
0
        bool OnBeforeRelsOtherEnds(IEnumerable <IEntity> entities, List <Action> postSaveActions)
        {
            using (Profiler.Measure("ResourceTriggerFilterEventTarget.OnBeforeDelete OnBeforeRelsOtherEnds"))
            {
                var policyCache = Factory.ResourceTriggerFilterPolicyCache;

                foreach (var entity in entities)
                {
                    //
                    // Get the filtered list of relationships that could possibly apply to some of the related entities
                    //
                    var filteredRels = GetRelsWithPotentialPolicies(policyCache, entity).ToList();

                    if (!filteredRels.Any())
                    {
                        continue;
                    }

                    // Prefill the cache
                    var requestString = string.Join(",", filteredRels.Select(r => (r.Item2 == Direction.Reverse ? "-#" : "#") + r.Item1.Id + ".id"));
                    BulkPreloader.Preload(new EntityRequest(entity.Id, requestString));

                    //
                    // Check each entity on the other end of the filtered rels t see if any policies apply
                    foreach (var relDir in filteredRels)
                    {
                        var rel       = relDir.Item1;
                        var direction = relDir.Item2;

                        var relInstances = entity.GetRelationships(rel, direction);

                        foreach (var otherEntity in relInstances)
                        {
                            var otherType = otherEntity.TypeIds.First();

                            var policies = policyCache.TypePolicyMap[otherType].Where(p => p.UpdatedRelationshipsToTriggerOn.Contains(rel, EntityIdEqualityComparer <Relationship> .Instance));

                            foreach (var policy in policies)
                            {
                                var handler = policyCache.PolicyTypeHandlerMap[policy.TypeIds.First()];

                                bool result = false;
                                SecurityBypassContext.RunAsUser(() =>
                                {
                                    if (handler.OnBeforeReverseRemove(policy, rel.Id, direction.Reverse(), otherEntity, entity))
                                    {
                                        result = true;
                                    }
                                    else
                                    {
                                        AddWrappedPostSaveAction(postSaveActions, () => handler.OnAfterReverseRemove(policy, rel.Id, direction.Reverse(), otherEntity, entity));
                                    }
                                });

                                if (result)
                                {
                                    return(true);
                                }
                            }
                        }
                    }
                }
                return(false);
            }
        }
示例#20
0
        bool EvaluateReverseTriggers(IResourceTriggerFilterPolicyCache policyCache, IEntity entity, IEnumerable <long> changedFields, IEnumerable <long> changedRels, IEnumerable <long> changedRevRels, List <Action> postSaveActions)
        {
            using (Profiler.Measure("ResourceTriggerFilterEventTarget.EvaluateReverseTriggers"))
            {
                //
                // Check if any of the related changed resources match any of our policies
                // TODO: Can we filter this earlier so I don't have to grab the type of every other end?
                var allChangedRels = changedRels.Select(rId => new { Id = rId, Direction = Direction.Forward }).Union(changedRevRels.Select(rId => new { Id = rId, Direction = Direction.Reverse }));
                var relChanges     = allChangedRels.ToDictionary(rd => rd, rd => entity.GetRelationships(rd.Id, rd.Direction)); // TODO: Can we make sure that all the type information for the entities is grabbed in a single hit? (It might already be happening)

                foreach (var kvp in relChanges)
                {
                    var relId         = kvp.Key.Id;
                    var direction     = kvp.Key.Direction;
                    var otherEntities = kvp.Value;

                    using (Profiler.Measure("ResourceTriggerFilterEventTarget.EvaluateReverseTriggers adds"))
                    {
                        //
                        // Test for any added entities that a policy may apply to
                        //
                        var added = GetAdded(otherEntities);

                        foreach (var otherEntity in added)
                        {
                            if (otherEntity != null)
                            {
                                bool result = false;

                                TestOtherEndPolicyAndAct(policyCache, relId, direction, entity, otherEntity, (policy) =>
                                {
                                    var handler = policyCache.PolicyTypeHandlerMap[policy.TypeIds.First()];

                                    SecurityBypassContext.RunAsUser(() =>
                                    {
                                        if (handler.OnBeforeReverseAdd(policy, relId, direction.Reverse(), otherEntity, entity, entity.IsTemporaryId))
                                        {
                                            result = true;
                                        }
                                        else
                                        {
                                            AddWrappedPostSaveAction(postSaveActions, () => handler.OnAfterReverseAdd(policy, relId, direction.Reverse(), otherEntity, entity, entity.IsTemporaryId));
                                        }
                                    });
                                });

                                if (result)
                                {
                                    return(true); // It failed so bug out
                                }
                            }
                        }
                    }

                    using (Profiler.Measure("ResourceTriggerFilterEventTarget.EvaluateReverseTriggers removes"))
                    {
                        //
                        // Test for any removed policies that might apply to
                        //
                        var removed = GetRemoved(otherEntities);

                        foreach (var otherEntity in removed)
                        {
                            if (otherEntity != null)
                            {
                                bool result = false;

                                TestOtherEndPolicyAndAct(policyCache, relId, direction, entity, otherEntity, (policy) =>
                                {
                                    var handler = policyCache.PolicyTypeHandlerMap[policy.TypeIds.First()];

                                    SecurityBypassContext.RunAsUser(() =>
                                    {
                                        if (handler.OnBeforeReverseRemove(policy, relId, direction.Reverse(), otherEntity, entity))
                                        {
                                            result = true;
                                        }
                                        else
                                        {
                                            AddWrappedPostSaveAction(postSaveActions, () => handler.OnAfterReverseRemove(policy, relId, direction.Reverse(), otherEntity, entity));
                                        }
                                    });
                                });

                                if (result)
                                {
                                    return(true); // It failed so bug out
                                }
                            }
                        }
                    }
                }
            }


            return(false);
        }