/// <summary> /// Verifies the user is authorized to submit the current <see cref="ChangeSet"/>. /// </summary> /// <returns>True if the <see cref="ChangeSet"/> is authorized, false otherwise.</returns> protected virtual bool AuthorizeChangeSet() { foreach (ChangeSetEntry changeSetEntry in ChangeSet.ChangeSetEntries) { if (!changeSetEntry.ActionDescriptor.Authorize(ActionContext)) { return(false); } // if there are any custom method invocations for this operation // we need to authorize them as well if (changeSetEntry.EntityActions != null && changeSetEntry.EntityActions.Any()) { Type entityType = changeSetEntry.Entity.GetType(); foreach (var entityAction in changeSetEntry.EntityActions) { UpdateActionDescriptor customAction = Description.GetCustomMethod(entityType, entityAction.Key); if (!customAction.Authorize(ActionContext)) { return(false); } } } } return(!ChangeSet.HasError); }
private static void ValidateAction(UpdateActionDescriptor updateAction) { // Only authorization filters are supported on CUD actions. This will capture 99% of user errors. // There is the chance that someone might attempt to implement an attribute that implements both // IAuthorizationFilter AND another filter type, but we don't want to have a black-list of filter // types here. if (updateAction.GetFilters().Any(p => !typeof(AuthorizationFilterAttribute).IsAssignableFrom(p.GetType()))) { throw Error.NotSupported(Resource.InvalidAction_UnsupportedFilterType, updateAction.ControllerDescriptor.ControllerType.Name, updateAction.ActionName); } }
private void InvokeCustomUpdateOperations() { foreach (ChangeSetEntry changeSetEntry in ChangeSet.ChangeSetEntries.Where(op => op.EntityActions != null && op.EntityActions.Any())) { Type entityType = changeSetEntry.Entity.GetType(); foreach (var entityAction in changeSetEntry.EntityActions) { UpdateActionDescriptor customUpdateAction = Description.GetCustomMethod(entityType, entityAction.Key); List <object> customMethodParams = new List <object>(entityAction.Value); customMethodParams.Insert(0, changeSetEntry.Entity); InvokeAction(customUpdateAction, customMethodParams.ToArray(), changeSetEntry); } } }
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext) { // first check to see if this is a call to Submit string actionName; if (controllerContext.RouteData.Values.TryGetValue(ActionRouteKey, out actionName) && actionName.Equals(SubmitActionValue, StringComparison.Ordinal)) { return(new SubmitActionDescriptor(controllerContext.ControllerDescriptor, controllerContext.Controller.GetType())); } // next check to see if this is a direct invocation of a CUD action DomainControllerDescription description = DomainControllerDescription.GetDescription(controllerContext.ControllerDescriptor); UpdateActionDescriptor action = description.GetUpdateAction(actionName); if (action != null) { return(new SubmitProxyActionDescriptor(action)); } return(base.SelectAction(controllerContext)); }
/// <summary> /// For all operations in the current changeset, validate that the operation exists, and /// set the operation entry. /// </summary> internal static void ResolveActions(DomainControllerDescription description, IEnumerable <ChangeSetEntry> changeSet) { // Resolve and set the action for each operation in the changeset foreach (ChangeSetEntry changeSetEntry in changeSet) { Type entityType = changeSetEntry.Entity.GetType(); UpdateActionDescriptor actionDescriptor = null; if (changeSetEntry.Operation == ChangeOperation.Insert || changeSetEntry.Operation == ChangeOperation.Update || changeSetEntry.Operation == ChangeOperation.Delete) { actionDescriptor = description.GetUpdateAction(entityType, changeSetEntry.Operation); } // if a custom method invocation is specified, validate that the method exists bool isCustomUpdate = false; if (changeSetEntry.EntityActions != null && changeSetEntry.EntityActions.Any()) { var entityAction = changeSetEntry.EntityActions.Single(); UpdateActionDescriptor customMethodOperation = description.GetCustomMethod(entityType, entityAction.Key); if (customMethodOperation == null) { throw Error.InvalidOperation(Resource.DomainController_InvalidAction, entityAction.Key, entityType.Name); } // if the primary action for an update is null but the entry // contains a valid custom update action, its considered a "custom update" isCustomUpdate = actionDescriptor == null && customMethodOperation != null; } if (actionDescriptor == null && !isCustomUpdate) { throw Error.InvalidOperation(Resource.DomainController_InvalidAction, changeSetEntry.Operation.ToString(), entityType.Name); } changeSetEntry.ActionDescriptor = actionDescriptor; } }
public SubmitProxyActionDescriptor(UpdateActionDescriptor updateAction) : base(updateAction.ControllerDescriptor, updateAction.ControllerDescriptor.ControllerType.GetMethod("Submit", BindingFlags.Instance | BindingFlags.Public)) { _updateAction = updateAction; }
private static DomainControllerDescription CreateDescription(HttpControllerDescriptor controllerDescriptor) { Type DomainControllerType = controllerDescriptor.ControllerType; MetadataProvider metadataProvider = CreateMetadataProvider(DomainControllerType); // get all public candidate methods and create the operations HashSet <Type> entityTypes = new HashSet <Type>(); List <UpdateActionDescriptor> actions = new List <UpdateActionDescriptor>(); IEnumerable <MethodInfo> methodsToInspect = DomainControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public) .Where(p => (p.DeclaringType != typeof(DomainController) && (p.DeclaringType != typeof(object))) && !p.IsSpecialName); foreach (MethodInfo method in methodsToInspect) { if (method.GetCustomAttributes(typeof(NonActionAttribute), false).Length > 0) { continue; } if (method.IsVirtual && method.GetBaseDefinition().DeclaringType == typeof(DomainController)) { // don't want to infer overrides of DomainController virtual methods as // operations continue; } // We need to ensure the buddy metadata provider is registered BEFORE we // attempt to do convention, since we rely on IsEntity which relies on // KeyAttributes being present (possibly from "buddy" classes) RegisterAssociatedMetadataProvider(method); ChangeOperation operationType = ClassifyUpdateOperation(method, metadataProvider); if (operationType != ChangeOperation.None) { Type entityType = method.GetParameters()[0].ParameterType; UpdateActionDescriptor actionDescriptor = new UpdateActionDescriptor(controllerDescriptor, method, entityType, operationType); ValidateAction(actionDescriptor); actions.Add(actionDescriptor); // TODO : currently considering entity types w/o any query methods // exposing them. Should we? if (metadataProvider.IsEntityType(entityType)) { AddEntityType(entityType, entityTypes, metadataProvider); } } else { // if the method is a "query" operation returning an entity, // add to entity types if (method.ReturnType != typeof(void)) { Type returnType = TypeUtility.UnwrapTaskInnerType(method.ReturnType); Type elementType = TypeUtility.GetElementType(returnType); if (metadataProvider.IsEntityType(elementType)) { AddEntityType(elementType, entityTypes, metadataProvider); } } } } return(new DomainControllerDescription(DomainControllerType, entityTypes, actions)); }
private static DomainControllerDescription CreateDescription(HttpControllerDescriptor controllerDescriptor) { Type DomainControllerType = controllerDescriptor.ControllerType; MetadataProvider metadataProvider = CreateMetadataProvider(DomainControllerType); // get all public candidate methods and create the operations HashSet<Type> entityTypes = new HashSet<Type>(); List<UpdateActionDescriptor> actions = new List<UpdateActionDescriptor>(); IEnumerable<MethodInfo> methodsToInspect = DomainControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public) .Where(p => (p.DeclaringType != typeof(DomainController) && (p.DeclaringType != typeof(object))) && !p.IsSpecialName); foreach (MethodInfo method in methodsToInspect) { if (method.GetCustomAttributes(typeof(NonActionAttribute), false).Length > 0) { continue; } if (method.IsVirtual && method.GetBaseDefinition().DeclaringType == typeof(DomainController)) { // don't want to infer overrides of DomainController virtual methods as // operations continue; } // We need to ensure the buddy metadata provider is registered BEFORE we // attempt to do convention, since we rely on IsEntity which relies on // KeyAttributes being present (possibly from "buddy" classes) RegisterAssociatedMetadataProvider(method); ChangeOperation operationType = ClassifyUpdateOperation(method, metadataProvider); if (operationType != ChangeOperation.None) { Type entityType = method.GetParameters()[0].ParameterType; UpdateActionDescriptor actionDescriptor = new UpdateActionDescriptor(controllerDescriptor, method, entityType, operationType); ValidateAction(actionDescriptor); actions.Add(actionDescriptor); // TODO : currently considering entity types w/o any query methods // exposing them. Should we? if (metadataProvider.IsEntityType(entityType)) { AddEntityType(entityType, entityTypes, metadataProvider); } } else { // if the method is a "query" operation returning an entity, // add to entity types if (method.ReturnType != typeof(void)) { Type returnType = TypeUtility.UnwrapTaskInnerType(method.ReturnType); Type elementType = TypeUtility.GetElementType(returnType); if (metadataProvider.IsEntityType(elementType)) { AddEntityType(elementType, entityTypes, metadataProvider); } } } } return new DomainControllerDescription(DomainControllerType, entityTypes, actions); }