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); } } }
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++; } } } } } } }