Skip to content

Instantly share code, notes, and snippets.

Created February 26, 2014 19:54
Show Gist options
  • Save anonymous/9237151 to your computer and use it in GitHub Desktop.
Save anonymous/9237151 to your computer and use it in GitHub Desktop.
public abstract class AuthorizationQueryValidator : ODataQueryValidator
{
public override void Validate(ODataQueryOptions options, ODataValidationSettings validationSettings)
{
base.Validate(options, validationSettings);
CanAccess(options.Request.GetODataPath().EntitySet);
SelectExpandClause selectExpandClause = options.SelectExpand.SelectExpandClause;
IList<ExpandedNavigationSelectItem> expandedItems = GetExpandedProperties(selectExpandClause);
foreach (ExpandedNavigationSelectItem item in expandedItems)
{
ValidatePath(item);
}
}
private void ValidatePath(ExpandedNavigationSelectItem item)
{
if (CanAccess(item.EntitySet))
{
foreach (var element in GetExpandedProperties(item.SelectAndExpand))
{
ValidatePath(element);
}
}
else
{
throw new InvalidOperationException(string.Format("Can't access the entitySet {0}", item.EntitySet.Name));
}
}
Up to here is the interesting part
private static IEdmNavigationProperty GetNavigationProperty(ExpandedNavigationSelectItem item)
{
return item.PathToNavigationProperty.OfType<NavigationPropertySegment>().Last().NavigationProperty;
}
public abstract bool CanAccess(IEdmNavigationSource navigationSource);
private static IList<ExpandedNavigationSelectItem> GetExpandedProperties(SelectExpandClause clause)
{
Contract.Assert(clause != null);
return clause.SelectedItems.OfType<ExpandedNavigationSelectItem>().ToList();
}
}
That is the abstract class that I defined just to try several options. This could be a way to do it:
public class AuthorizationAnnotation
{
public string[] AllowedRoles { get; private set; }
public AuthorizationAnnotation(params string[] allowedRoles)
{
AllowedRoles = allowedRoles;
}
}
public class StaticAuthorizationQueryValidator : AuthorizationQueryValidator
{
private HttpRequestMessage _request;
public StaticAuthorizationQueryValidator(HttpRequestMessage request)
{
_request = request;
}
public override bool CanAccess(IEdmNavigationSource edmEntitySetBase)
{
IEdmModel model = _request.GetEdmModel();
AuthorizationAnnotation authorizedRoles = model.GetAnnotationValue<AuthorizationAnnotation>(edmEntitySetBase);
return authorizedRoles == null ||
authorizedRoles.AllowedRoles.Any(rol => rol == "*" || _request.GetRequestContext().Principal.IsInRole(rol));
}
}
Here is how you would configure the model:
public static IEdmModel GetModel()
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Customer>("Customers");
builder.EntitySet<Order>("Orders");
builder.EntitySet<Address>("Addresses");
builder.EntitySet<OrderLine>("OrdersLines");
IEdmModel model = builder.GetEdmModel();
SetRoles(model, "Customers", "*");
SetRoles(model, "Orders", "Administrator");
SetRoles(model, "Addresses", "EspecialRole");
return model;
}
private static void SetRoles(IEdmModel model, string navigationSourceName, params string[] roles)
{
IEdmNavigationSource navigationSource = model.EntityContainer.FindEntitySet(navigationSourceName);
if (navigationSource == null)
{
navigationSource = model.EntityContainer.FindSingleton(navigationSourceName);
}
if (navigationSource == null)
{
throw new ArgumentException("The given entity set or singleton doesn't exist", "navigationSourceName");
}
model.SetAnnotationValue(navigationSource, new AuthorizationAnnotation(roles));
}
Plumbing through the QueryableAttribute:
public class ValidatingQueryableAttribute : QueryableAttribute
{
public override void ValidateQuery(HttpRequestMessage request, ODataQueryOptions queryOptions)
{
queryOptions.Validator = new StaticAuthorizationQueryValidator(request);
base.ValidateQuery(request, queryOptions);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment