Example #1
0
        public EntityMutationInfo(IEntityType entityType, JObject jObject, MutationContext mutationContext)
        {
            IsReference            = jObject.ContainsKey("$ref");
            JObject                = jObject;
            MutationContext        = mutationContext;
            EntityType             = entityType;
            Properties             = new Dictionary <IProperty, PropertyMutationInfo>();
            References             = new Dictionary <INavigation, ReferenceMutationInfo>();
            Collections            = new Dictionary <INavigation, CollectionMutationInfo>();
            Entity                 = Activator.CreateInstance(entityType.ClrType);
            RuleMap                = mutationContext.RuleMaps.Where(ruleMap => ruleMap.CanHandle(entityType.ClrType, mutationContext.SecurityContext)).BuildRuleMap();
            PermissionType         = mutationContext.PermissionEntityTypeBuilder.TypeMap[entityType];
            SelfPermissionProperty = mutationContext.PermissionEntityTypeBuilder.TypeMap[entityType].GetProperty("SelfPermission");
            PrimaryKey             = entityType.FindPrimaryKey();

            var jProperties = jObject.Properties();
            var properties  = entityType.GetProperties();
            var navigations = entityType.GetNavigations();
            var references  = navigations.Where(navigation => !navigation.IsCollection());
            var collections = navigations.Where(navigation => navigation.IsCollection());

            Properties  = properties.ToDictionary(property => property, property => new PropertyMutationInfo(MutationContext, property, this));
            References  = references.ToDictionary(reference => reference, reference => new ReferenceMutationInfo(MutationContext, reference, this));
            Collections = collections.ToDictionary(collection => collection, collection => new CollectionMutationInfo(MutationContext, collection, this));


            PropertyTuples = jProperties.Join(properties, jp => jp.Name.ToPascalCase(), p => p.Name, (jp, p) => new PropertyPropertyTuple {
                JProperty = jp, Property = p
            }).ToHashSet();
            ReferenceTuples = jProperties.Join(references, jp => jp.Name.ToPascalCase(), p => p.Name, (jp, p) => new NavigationPropertyTuple {
                JProperty = jp, Navigation = p
            }).ToHashSet();
            CollectionTuples = jProperties.Join(collections, jp => jp.Name.ToPascalCase(), p => p.Name, (jp, p) => new NavigationPropertyTuple {
                JProperty = jp, Navigation = p
            }).Where(tuple => tuple.JProperty.Values().Any()).ToHashSet();

            var primaryKeyTuples           = PrimaryKey.Properties.Join(PropertyTuples, pkp => pkp, pt => pt.Property, (pkp, pt) => pkp).ToArray();
            var isPrimaryKeySet            = primaryKeyTuples.Length == PrimaryKey.Properties.Count;
            var nonGeneratedPropertyTuples = PropertyTuples.Where(pt => !pt.Property.ValueGenerated.HasFlag(ValueGenerated.OnAdd));

            if (jObject.ContainsKey("$isCreation"))
            {
                State = EntityState.Added;
            }
            else if (isPrimaryKeySet)
            {
                if (jObject.ContainsKey("$isDeletion"))
                {
                    State = EntityState.Deleted;
                }
                else if (jObject.ContainsKey("$isEdition") || nonGeneratedPropertyTuples.Any() || References.Values.Any(@ref => @ref.IsModified) || Collections.Values.Any(col => col.IsModified))
                {
                    State = EntityState.Modified;
                }
                else
                {
                    State = EntityState.Unchanged;
                }
            }
            else if (PrimaryKey.Properties[0].ValueGenerated == ValueGenerated.OnAdd)
            {
                State = EntityState.Added;
            }
            else
            {
                State = EntityState.Unchanged;
            }

            foreach (var propertyTuple in PropertyTuples)
            {
                var value = propertyTuple.JProperty.Value.ToObject(propertyTuple.Property.ClrType, mutationContext.JsonSerializer);
                propertyTuple.Property.AsProperty().Setter.SetClrValue(Entity, value);
                Properties[propertyTuple.Property].IsModified = !(propertyTuple.Property.ValueGenerated.HasFlag(ValueGenerated.OnAdd) && propertyTuple.Property.IsPrimaryKey());
                Properties[propertyTuple.Property].IsTouched  = true;
                var containingForeignKeys = propertyTuple.Property.GetContainingForeignKeys();
                foreach (var containingForeignKey in containingForeignKeys)
                {
                    var navigation = containingForeignKey.GetNavigation(true);
                    if (navigation != null)
                    {
                        var reference = References[containingForeignKey.GetNavigation(true)];
                        reference.IsModified = true;
                        var keyProperty1 = propertyTuple.Property.FindFirstPrincipal();
                        if (reference.TargetEntityMutationInfo == null)
                        {
                            var propertyValue = (JObject)jObject.Property(reference.Navigation.Name.ToCamelCase())?.Value;
                            if (propertyValue == null)
                            {
                                propertyValue = new JObject();
                            }
                            if (!propertyValue.ContainsKey(keyProperty1.Name.ToCamelCase()))
                            {
                                propertyValue.Add(keyProperty1.Name.ToCamelCase(), JToken.FromObject(value));
                            }
                            reference.TargetEntityMutationInfo      = new EntityMutationInfo(containingForeignKey.PrincipalEntityType, propertyValue, MutationContext);
                            reference.TargetEntityMutationInfo.Root = State == EntityState.Added;
                        }
                        else
                        {
                            keyProperty1.AsProperty().Setter.SetClrValue(reference.TargetEntityMutationInfo.Entity, propertyTuple.Property.GetGetter().GetClrValue(Entity));
                        }
                    }
                }
            }
            foreach (var referenceTuple in ReferenceTuples)
            {
                var referencedEntityMutationInfo = References[referenceTuple.Navigation].TargetEntityMutationInfo;
                if (!referenceTuple.Navigation.IsDependentToPrincipal() && isPrimaryKeySet)
                {
                    foreach (var fk in referenceTuple.Navigation.ForeignKey.Properties)
                    {
                        (referenceTuple.JProperty.Value as JObject)[fk.Name] = new JValue(fk.FindFirstPrincipal().AsProperty().Getter.GetClrValue(Entity));
                    }
                }
                if (referencedEntityMutationInfo == null)
                {
                    References[referenceTuple.Navigation].TargetEntityMutationInfo = referencedEntityMutationInfo = new EntityMutationInfo(referenceTuple.Navigation.GetTargetType(), (JObject)referenceTuple.JProperty.Value, MutationContext);
                    referencedEntityMutationInfo.Root = State == EntityState.Added;
                }
                References[referenceTuple.Navigation].IsModified = true;
                References[referenceTuple.Navigation].IsTouched  = true;
                referenceTuple.Navigation.AsNavigation().Setter.SetClrValue(Entity, References[referenceTuple.Navigation].TargetEntityMutationInfo.Entity);
                foreach (var foreignKeyProperty in referenceTuple.Navigation.ForeignKey.Properties)
                {
                    var isDependentToPrincipal      = referenceTuple.Navigation.IsDependentToPrincipal();
                    var fixedForeignKeyProperty     = isDependentToPrincipal ? foreignKeyProperty : foreignKeyProperty.FindFirstPrincipal();
                    var principalForeignKeyProperty = isDependentToPrincipal ? foreignKeyProperty.FindFirstPrincipal() : foreignKeyProperty;
                    if (!Properties[fixedForeignKeyProperty].IsTouched)
                    {
                        Properties[fixedForeignKeyProperty].IsTouched  = true;
                        Properties[fixedForeignKeyProperty].IsModified = true;
                        fixedForeignKeyProperty.AsProperty().Setter.SetClrValue(Entity, principalForeignKeyProperty.AsProperty().Getter.GetClrValue(referencedEntityMutationInfo.Entity));
                    }
                }
                var inverseNavigation = referenceTuple.Navigation.FindInverse();
                if (inverseNavigation != null)
                {
                    if (inverseNavigation.IsCollection())
                    {
                        var inverseReferenceMutationInfo = referencedEntityMutationInfo.Collections[inverseNavigation];
                        inverseReferenceMutationInfo.TargetEntityMutationInfos.Add(this);
                        inverseReferenceMutationInfo.IsModified = true;
                        inverseReferenceMutationInfo.IsTouched  = true;
                        inverseNavigation.AsNavigation().CollectionAccessor.Add(referencedEntityMutationInfo.Entity, Entity, false);
                    }
                    else
                    {
                        var inverseReferenceMutationInfo = referencedEntityMutationInfo.References[inverseNavigation];
                        inverseReferenceMutationInfo.TargetEntityMutationInfo = this;
                        inverseReferenceMutationInfo.IsModified = true;
                        inverseReferenceMutationInfo.IsTouched  = true;
                        inverseNavigation.AsNavigation().Setter.SetClrValue(referencedEntityMutationInfo.Entity, Entity);
                    }
                }
            }
            foreach (var collectionTuple in CollectionTuples)
            {
                var targetEntityMutationInfos = collectionTuple.JProperty.Values().Select(value => new EntityMutationInfo(collectionTuple.Navigation.GetTargetType(), (JObject)value, MutationContext)).ToHashSet();
                Collections[collectionTuple.Navigation].IsModified = true;
                Collections[collectionTuple.Navigation].IsTouched  = true;
                var foreignKeyProperties = collectionTuple.Navigation.ForeignKey.Properties;
                foreach (var targetEntityMutationInfo in targetEntityMutationInfos)
                {
                    Collections[collectionTuple.Navigation].TargetEntityMutationInfos.Add(targetEntityMutationInfo);
                    foreach (var foreignKeyProperty in foreignKeyProperties)
                    {
                        var referenceKeyProperty           = foreignKeyProperty.FindFirstPrincipal();
                        var foreignKeyPropertyMutationInfo = targetEntityMutationInfo.Properties[foreignKeyProperty];
                        foreignKeyPropertyMutationInfo.IsModified = true;
                        foreignKeyPropertyMutationInfo.IsTouched  = true;
                        foreignKeyProperty.AsProperty().Setter.SetClrValue(targetEntityMutationInfo.Entity, referenceKeyProperty.AsProperty().Getter.GetClrValue(Entity));
                    }
                    var inverseNavigation = collectionTuple.Navigation.FindInverse();
                    if (inverseNavigation != null)
                    {
                        var inverseReferenceMutationInfo = targetEntityMutationInfo.References[inverseNavigation];
                        inverseReferenceMutationInfo.IsModified = true;
                        inverseReferenceMutationInfo.IsTouched  = true;
                        inverseReferenceMutationInfo.TargetEntityMutationInfo = this;
                        inverseNavigation.AsNavigation().Setter.SetClrValue(targetEntityMutationInfo.Entity, Entity);
                    }
                    collectionTuple.Navigation.AsNavigation().CollectionAccessor.Add(Entity, targetEntityMutationInfo.Entity, false);
                }
            }
        }
Example #2
0
        public void CheckPermissions(object context, object permissionEntity, ISet <EntityMutationInfo> cache)
        {
            if (!(IsReference || cache.Contains(this)))
            {
                cache.Add(this);
                var permissionEntityType = permissionEntity?.GetType();
                var primaryKeyPairs      = PropertyTuples.Where(pt => PrimaryKey.Properties.Any(pk => pk == pt.Property));
                var primaryKeyTuples     = PrimaryKey.Properties.Join(PropertyTuples, pk => pk, pt => pt.Property, (pk, pt) => pt);

                var requiredPermission = State.ToPermission();
                var typePermission     = (bool?)RuleMap.ResolveTypeCustom(requiredPermission, context);
                var instancePermission = (bool?)permissionEntityType?.GetProperty("SelfPermission")?.GetValue(permissionEntity)
                                         ?? (bool?)RuleMap.ResolveInstanceCustom(context, Entity, requiredPermission);

                if (instancePermission != null && !instancePermission.Value)
                {
                    throw new UnauthorizedAccessException($"You do not have permission to {Enum.GetName(typeof(Permission), requiredPermission)} on entity type {EntityType.Name} with id with key ({string.Join(", ", primaryKeyPairs.Select(kp => $"{kp.Property.Name}={kp.JProperty.Value}"))}).");
                }

                if (instancePermission == null && (typePermission == null || !typePermission.Value))
                {
                    throw new UnauthorizedAccessException($"You do not have permission to {Enum.GetName(typeof(Permission), requiredPermission)} on entity type {EntityType.Name}.");
                }

                var globalPermission = instancePermission ?? typePermission;

                foreach (var primaryKeyPropertyTuple in primaryKeyTuples)
                {
                    var propertyPermission         = (bool?)RuleMap.ResolvePropertyCustom(requiredPermission, primaryKeyPropertyTuple.Property.PropertyInfo, context);
                    var instancePropertyPermission = (bool?)permissionEntityType?.GetProperty($"{primaryKeyPropertyTuple.Property.Name}Permission")?.GetValue(permissionEntity)
                                                     ?? (bool?)RuleMap.ResolveInstancePropertyCustom(primaryKeyPropertyTuple.Property.PropertyInfo, Permission.Read, context, Entity);
                    if (instancePropertyPermission != null && !instancePropertyPermission.Value)
                    {
                    }
                    if (instancePropertyPermission == null && (globalPermission == null || !globalPermission.Value) && (propertyPermission == null || !propertyPermission.Value))
                    {
                        throw new UnauthorizedAccessException($"You do not have permission to {Enum.GetName(typeof(Permission), Permission.Read)} on property {primaryKeyPropertyTuple.Property.Name} on entity type {EntityType.Name}.");
                    }
                }

                foreach (var property in PropertyTuples.Select(pt => pt.Property.AsPropertyBase()).Union(ReferenceTuples.Union(CollectionTuples).Select(nt => nt.Navigation.AsPropertyBase())))
                {
                    var propertyPermission         = (bool?)RuleMap.ResolvePropertyCustom(requiredPermission, property.PropertyInfo, context);
                    var instancePropertyPermission = (bool?)permissionEntityType?.GetProperty($"{property.Name}Permission")?.GetValue(permissionEntity)
                                                     ?? (bool?)RuleMap.ResolveInstancePropertyCustom(property.PropertyInfo, requiredPermission, context, Entity);
                    if (instancePropertyPermission != null && !instancePropertyPermission.Value)
                    {
                        throw new UnauthorizedAccessException($"You do not have permission to {Enum.GetName(typeof(Permission), requiredPermission)} on property {property.Name} on entity type {EntityType.Name} with key ({string.Join(", ", primaryKeyPairs.Select(kp => $"{kp.Property.Name}={kp.JProperty.Value}"))}).");
                    }
                    if (instancePropertyPermission == null && (globalPermission == null || !globalPermission.Value) && (propertyPermission == null || !propertyPermission.Value))
                    {
                        throw new UnauthorizedAccessException($"You do not have permission to {Enum.GetName(typeof(Permission), requiredPermission)} on property {property.Name} on entity type {EntityType.Name}.");
                    }
                }

                foreach (var reference in References.Values.Where(reference => reference.TargetEntityMutationInfo != null))
                {
                    reference.TargetEntityMutationInfo.CheckPermissions(context, permissionEntityType?.GetProperty(reference.Navigation.Name)?.GetValue(permissionEntity), cache);
                }

                foreach (var collectionMutationInfo in Collections.Values)
                {
                    if (permissionEntity != null)
                    {
                        var collection = (object[])MutationContext.PermissionEntityTypeBuilder.PropertyMap[collectionMutationInfo.Navigation].GetValue(permissionEntity);
                        if (collection != null)
                        {
                            var index = 0;
                            foreach (var itemMutationInfo in collectionMutationInfo.TargetEntityMutationInfos)
                            {
                                if (itemMutationInfo.State != EntityState.Added)
                                {
                                    itemMutationInfo.CheckPermissions(context, collection[index], cache);
                                    index++;
                                }
                            }
                        }
                    }
                }
            }
        }