private static IScopesEvaluator GetNavigationPropertyPropertyOperationPermisions(IList <ODataPathSegment> pathSegments, bool isTargetByKey, IEdmModel model, string method) { if (pathSegments.Count <= 1) { return(new DefaultScopesEvaluator()); } var expectedPath = GetPathFromSegments(pathSegments); IEdmVocabularyAnnotatable root = (pathSegments[0] as EntitySetSegment).EntitySet as IEdmVocabularyAnnotatable ?? (pathSegments[0] as SingletonSegment).Singleton; var navRestrictions = root.VocabularyAnnotations(model).Where(a => a.Term.FullName() == ODataCapabilityRestrictionsConstants.NavigationRestrictions); foreach (var restriction in navRestrictions) { if (restriction.Value is IEdmRecordExpression record) { var temp = record.FindProperty("RestrictedProperties"); if (temp?.Value is IEdmCollectionExpression restrictedProperties) { foreach (var item in restrictedProperties.Elements) { if (item is IEdmRecordExpression restrictedProperty) { var navigationProperty = restrictedProperty.FindProperty("NavigationProperty").Value as IEdmPathExpression; if (navigationProperty?.Path == expectedPath) { if (method == "GET") { var readRestrictions = restrictedProperty.FindProperty("ReadRestrictions")?.Value as IEdmRecordExpression; var readPermissions = ExtractPermissionsFromRecord(readRestrictions); var evaluator = new WithOrScopesCombiner(readPermissions); if (isTargetByKey) { var readByKeyRestrictions = readRestrictions.FindProperty("ReadByKeyRestrictions")?.Value as IEdmRecordExpression; var readByKeyPermissions = ExtractPermissionsFromRecord(readByKeyRestrictions); evaluator.AddRange(readByKeyPermissions); } return(evaluator); } else if (method == "POST" || method == "PATCH" || method == "PUT" || method == "MERGE" || method == "DELETE") { var updateRestrictions = restrictedProperty.FindProperty("UpdateRestrictions")?.Value as IEdmRecordExpression; var updatePermissions = ExtractPermissionsFromRecord(updateRestrictions); return(new WithOrScopesCombiner(updatePermissions)); } } } } } } } return(new DefaultScopesEvaluator()); }
private static IScopesEvaluator GetReadByKeyPermissions(IEnumerable <IEdmVocabularyAnnotation> annotations) { var evaluator = new WithOrScopesCombiner(); foreach (var annotation in annotations) { if (annotation.Term.FullName() == ODataCapabilityRestrictionsConstants.ReadRestrictions && annotation.Value is IEdmRecordExpression record) { var readPermissions = ExtractPermissionsFromAnnotation(annotation); evaluator.AddRange(readPermissions); var readByKeyProperty = record.FindProperty("ReadByKeyRestrictions"); var readByKeyValue = readByKeyProperty?.Value as IEdmRecordExpression; var permissionsProperty = readByKeyValue?.FindProperty("Permissions"); var readByKeyPermissions = ExtractPermissionsFromProperty(permissionsProperty); evaluator.AddRange(readByKeyPermissions); } } return(evaluator); }
internal static IScopesEvaluator ExtractPermissionsForRequest(this IEdmModel model, string method, AspNet.OData.Routing.ODataPath odataPath) { var template = odataPath.PathTemplate; ODataPathSegment prevSegment = null; var segments = new List <ODataPathSegment>(); // this combines the permission scopes across path segments // with a logical AND var permissionsChain = new WithAndScopesCombiner(); var lastSegmentIndex = odataPath.Segments.Count - 1; if (template.EndsWith("$ref", StringComparison.OrdinalIgnoreCase)) { // for ref segments, we apply the permission of the entity that contains the navigation property // e.g. for GET Customers(10)/Products/$ref, we apply the read key permissions of Customers // for GET TopCustomer/Products/$ref, we apply the read permissions of TopCustomer // for DELETE Customers(10)/Products(10)/$ref we apply the update permissions of Customers lastSegmentIndex = odataPath.Segments.Count - 2; while (!(odataPath.Segments[lastSegmentIndex] is KeySegment || odataPath.Segments[lastSegmentIndex] is SingletonSegment || odataPath.Segments[lastSegmentIndex] is NavigationPropertySegment) && lastSegmentIndex > 0) { lastSegmentIndex--; } } for (int i = 0; i <= lastSegmentIndex; i++) { var segment = odataPath.Segments[i]; if (segment is EntitySetSegment || segment is SingletonSegment || segment is NavigationPropertySegment || segment is OperationSegment || segment is OperationImportSegment || segment is KeySegment || segment is PropertySegment) { var parent = prevSegment; var isPropertyAccess = IsNextSegmentOfType <PropertySegment>(odataPath, i) || IsNextSegmentOfType <NavigationPropertyLinkSegment>(odataPath, i) || IsNextSegmentOfType <NavigationPropertySegment>(odataPath, i); prevSegment = segment; segments.Add(segment); // if nested segment, extract navigation restrictions of root // else extract entity/set restrictions if (segment is EntitySetSegment entitySetSegment) { // if Customers(key), then we'll handle it when we reach the key segment // so that we can properly handle ReadByKeyRestrictions if (IsNextSegmentKey(odataPath, i)) { continue; } // if Customers/UnboundFunction, then we'll handle it when we reach the operation segment if (IsNextSegmentOfType <OperationSegment>(odataPath, i)) { continue; } IScopesEvaluator permissions; permissions = GetNavigationPropertyCrudPermisions( segments, false, model, method); if (permissions is DefaultScopesEvaluator) { permissions = GetNavigationSourceCrudPermissions(entitySetSegment.EntitySet, model, method); } var handler = new WithOrScopesCombiner(permissions); permissionsChain.Add(handler); } else if (segment is SingletonSegment singletonSegment) { // if Customers/UnboundFunction, then we'll handle it when we reach the operation segment if (IsNextSegmentOfType <OperationSegment>(odataPath, i)) { continue; } if (isPropertyAccess) { var propertyPermissions = GetSingletonPropertyOperationPermissions(singletonSegment.Singleton, model, method); permissionsChain.Add(new WithOrScopesCombiner(propertyPermissions)); } else { var permissions = GetNavigationSourceCrudPermissions(singletonSegment.Singleton, model, method); permissionsChain.Add(new WithOrScopesCombiner(permissions)); } } else if (segment is KeySegment keySegment) { // if Customers/UnboundFunction, then we'll handle it when we reach the operation segment if (IsNextSegmentOfType <OperationSegment>(odataPath, i)) { continue; } var entitySet = keySegment.NavigationSource as IEdmEntitySet; var permissions = isPropertyAccess ? GetEntityPropertyOperationPermissions(entitySet, model, method) : GetEntityCrudPermissions(entitySet, model, method); var evaluator = new WithOrScopesCombiner(permissions); if (parent is NavigationPropertySegment) { var nestedPermissions = isPropertyAccess ? GetNavigationPropertyPropertyOperationPermisions(segments, isTargetByKey: true, model, method) : GetNavigationPropertyCrudPermisions(segments, isTargetByKey: true, model, method); evaluator.Add(nestedPermissions); } permissionsChain.Add(evaluator); } else if (segment is NavigationPropertySegment navSegment) { // if Customers/UnboundFunction, then we'll handle it when we reach there if (IsNextSegmentOfType <OperationSegment>(odataPath, i)) { continue; } // if Customers(key), then we'll handle it when we reach the key segment // so that we can properly handle ReadByKeyRestrictions if (IsNextSegmentKey(odataPath, i)) { continue; } var topLevelPermissions = GetNavigationSourceCrudPermissions(navSegment.NavigationSource as IEdmVocabularyAnnotatable, model, method); var segmentEvaluator = new WithOrScopesCombiner(topLevelPermissions); var nestedPermissions = GetNavigationPropertyCrudPermisions( segments, isTargetByKey: false, model, method); segmentEvaluator.Add(nestedPermissions); permissionsChain.Add(segmentEvaluator); } else if (segment is OperationImportSegment operationImportSegment) { var annotations = operationImportSegment.OperationImports.First().Operation.VocabularyAnnotations(model); var permissions = GetOperationPermissions(annotations); permissionsChain.Add(new WithOrScopesCombiner(permissions)); } else if (segment is OperationSegment operationSegment) { var annotations = operationSegment.Operations.First().VocabularyAnnotations(model); var operationPermissions = GetOperationPermissions(annotations); permissionsChain.Add(new WithOrScopesCombiner(operationPermissions)); } } } return(permissionsChain); }