public void MapMvcAttributeRoutes_DoesNotTryToInferRouteNames()
        {
            var controllerDescriptor = new ReflectedAsyncControllerDescriptor(typeof(MyController));

            var routeEntries = AttributeRoutingMapper.MapAttributeRoutes(controllerDescriptor);

            var routeEntry = Assert.Single(routeEntries);
            Assert.Null(routeEntry.Name);
        }
        /// <summary>
        /// Resolves the action method parameters.
        /// </summary>
        /// <param name="controllerTypeResolver">The controller type resolver.</param>
        /// <param name="areaName">Name of the area.</param>
        /// <param name="controllerName">Name of the controller.</param>
        /// <param name="actionMethodName">Name of the action method.</param>
        /// <returns>
        /// A action method parameters represented as a <see cref="string"/> instance
        /// </returns>
        public IEnumerable<string> ResolveActionMethodParameters(IControllerTypeResolver controllerTypeResolver,
                                                                 string areaName, string controllerName,
                                                                 string actionMethodName)
        {
            // Is the request cached?
            string cacheKey = areaName + "_" + controllerName + "_" + actionMethodName;
            if (Cache.ContainsKey(cacheKey))
            {
                return Cache[cacheKey];
            }

            // Get controller type
            Type controllerType = controllerTypeResolver.ResolveControllerType(areaName, controllerName);

            // Get action method information
            var actionParameters = new List<string>();
            if (controllerType != null)
            {
                ControllerDescriptor controllerDescriptor = null;
                if (typeof(IController).IsAssignableFrom(controllerType))
                {
                    controllerDescriptor = new ReflectedControllerDescriptor(controllerType);
                }
                else if (typeof(IAsyncController).IsAssignableFrom(controllerType))
                {
                    controllerDescriptor = new ReflectedAsyncControllerDescriptor(controllerType);
                }

                ActionDescriptor[] actionDescriptors = controllerDescriptor.GetCanonicalActions()
                    .Where(a => a.ActionName == actionMethodName).ToArray();

                if (actionDescriptors != null && actionDescriptors.Length > 0)
                {
                    foreach (ActionDescriptor actionDescriptor in actionDescriptors)
                    {
                        actionParameters.AddRange(actionDescriptor.GetParameters().Select(p => p.ParameterName));
                    }
                }
            }

            // Cache the result
            if (!Cache.ContainsKey(cacheKey))
            {
                try
                {
                    Cache.Add(cacheKey, actionParameters);
                }
                catch (ArgumentException)
                {
                    // Nomnomnom... We're intentionally eating it here
                }
            }

            // Return
            return actionParameters;
        }
        public void MapMvcAttributeRoutes_RespectsActionNameAttribute()
        {
            var controllerDescriptor = new ReflectedAsyncControllerDescriptor(typeof(MyController));
            var mapper = new AttributeRoutingMapper(new RouteBuilder());

            var routeEntries = mapper.MapMvcAttributeRoutes(controllerDescriptor);

            var routeEntry = Assert.Single(routeEntries);
            Assert.Equal("ActionName", routeEntry.Route.Defaults["action"]);
        }
        public void MapMvcAttributeRoutes_WithControllerRoute_AndNoReachableActions()
        {
            // Arrange
            var controllerDescriptor = new ReflectedAsyncControllerDescriptor(typeof(NoActionsController));

            // Act
            var entries = AttributeRoutingMapper.MapAttributeRoutes(controllerDescriptor);

            // Assert
            Assert.Empty(entries);
        }
        internal List<RouteEntry> MapMvcAttributeRoutes(ReflectedAsyncControllerDescriptor controllerDescriptor)
        {
            string prefix = GetPrefixFrom(controllerDescriptor);
            ValidatePrefixTemplate(prefix, controllerDescriptor);            

            RouteAreaAttribute area = GetAreaFrom(controllerDescriptor);
            string areaName = GetAreaName(controllerDescriptor, area);
            string areaPrefix = area != null ? area.AreaPrefix ?? area.AreaName : null;
            ValidateAreaPrefixTemplate(areaPrefix, areaName, controllerDescriptor);

            string controllerName = controllerDescriptor.ControllerName;

            AsyncActionMethodSelector actionSelector = controllerDescriptor.Selector;
            IEnumerable<MethodInfo> actionMethodsInfo = actionSelector.AliasedMethods
                                                                      .Concat(actionSelector.NonAliasedMethods.SelectMany(x => x))
                                                                      .Where(m => m.DeclaringType == controllerDescriptor.ControllerType);

            if (actionSelector.AllowLegacyAsyncActions)
            {
                // if the ActionAsync / ActionCompleted pattern is used, we need to remove the "Completed" methods
                // and not look up routing attributes on them
                actionMethodsInfo =
                    actionMethodsInfo.Where(m => !m.Name.EndsWith("Completed", StringComparison.OrdinalIgnoreCase));
            }

            List<RouteEntry> routeEntries = new List<RouteEntry>();

            foreach (var method in actionMethodsInfo)
            {
                string actionName = GetActionName(method, actionSelector.AllowLegacyAsyncActions);
                IEnumerable<IDirectRouteInfoProvider> routeAttributes = GetRouteAttributes(method);

                foreach (var routeAttribute in routeAttributes)
                {
                    ValidateTemplate(routeAttribute.RouteTemplate, actionName, controllerDescriptor);

                    string template = CombinePrefixAndAreaWithTemplate(areaPrefix, prefix, routeAttribute.RouteTemplate);
                    Route route = _routeBuilder.BuildDirectRoute(template, routeAttribute.Verbs, controllerName,
                                                                    actionName, method, areaName);
                    RouteEntry entry = new RouteEntry
                    {
                        Name = routeAttribute.RouteName,
                        Route = route,
                        Template = template,
                        ParsedRoute = RouteParser.Parse(route.Url),
                        Order = routeAttribute.RouteOrder                        
                    };
                    routeEntries.Add(entry);                    
                }
            }

            return routeEntries;
        }
 public ControllerDescriptor Create(Type controllerType)
 {
     ControllerDescriptor controllerDescriptor = null;
     if (typeof(IController).IsAssignableFrom(controllerType))
     {
         controllerDescriptor = new ReflectedControllerDescriptor(controllerType);
     }
     else if (typeof(IAsyncController).IsAssignableFrom(controllerType))
     {
         controllerDescriptor = new ReflectedAsyncControllerDescriptor(controllerType);
     }
     return controllerDescriptor;
 }
        public void MapMvcAttributeRoutes_WithControllerRoute()
        {
            // Arrange
            var controllerDescriptor = new ReflectedAsyncControllerDescriptor(typeof(AnotherController));

            // Act
            var entries = AttributeRoutingMapper.MapAttributeRoutes(controllerDescriptor);

            // Assert
            var controllerEntry = Assert.Single(entries.Where(r => !r.Route.Defaults.ContainsKey("action")));
            Assert.Same(controllerDescriptor, controllerEntry.Route.GetTargetControllerDescriptor());

            var actionMethods = controllerEntry.Route.GetTargetActionDescriptors().ToArray();
            Assert.Equal(2, actionMethods.Length);
            Assert.Single(actionMethods, a => a.ActionName == "RegularAction");
            Assert.Single(actionMethods, a => a.ActionName == "AnotherAction");
        }
        private static string GetPrefixFrom(ReflectedAsyncControllerDescriptor controllerDescriptor)
        {
            // this only happens once per controller type, for the lifetime of the application,
            // so we do not need to cache the results
            object[] routePrefixAttributes = controllerDescriptor.GetCustomAttributes(typeof(RoutePrefixAttribute), inherit: false);
            if (routePrefixAttributes.Length > 0)
            {
                RoutePrefixAttribute routePrefixAttribute = routePrefixAttributes[0] as RoutePrefixAttribute;
                if (routePrefixAttribute != null)
                {
                    return routePrefixAttribute.Prefix;
                }
            }

            return null;
        }
 private static RouteAreaAttribute GetAreaFrom(ReflectedAsyncControllerDescriptor controllerDescriptor)
 {
     RouteAreaAttribute areaAttribute =
         controllerDescriptor.GetCustomAttributes(typeof(RouteAreaAttribute), true)
                             .Cast<RouteAreaAttribute>()
                             .FirstOrDefault();
     return areaAttribute;
 }
        private static string GetAreaName(ReflectedAsyncControllerDescriptor controllerDescriptor, RouteAreaAttribute area)
        {
            if (area == null)
            {
                return null;
            }

            if (area.AreaName != null)
            {
                return area.AreaName;
            }
            if (controllerDescriptor.ControllerType.Namespace != null)
            {
                return controllerDescriptor.ControllerType.Namespace.Split('.').Last();
            }

            throw Error.InvalidOperation(MvcResources.AttributeRouting_CouldNotInferAreaNameFromMissingNamespace, controllerDescriptor.ControllerName);
        }
        public bool IsAccessibleToUser(IControllerTypeResolver controllerTypeResolver, DefaultSiteMapProvider provider,
            HttpContext context, SiteMapNode node)
        {
            // is security trimming enabled?
            if (!provider.SecurityTrimmingEnabled)
            {
                return true;
            }

            // external nodes
            var nodeUrl = node.Url;
            if (nodeUrl.StartsWith("http") || nodeUrl.StartsWith("ftp"))
            {
                return true;
            }

            // Is it a regular node?
            var mvcNode = node as MvcSiteMapNode;
            if (mvcNode == null)
            {
                return true;
            }

            var controllerType = controllerTypeResolver.ResolveControllerType(mvcNode.Area, mvcNode.Controller);
            if (controllerType == null)
            {
                return false;
            }

            // Find routes for the sitemap node's url
            HttpContextBase httpContext = new HttpContextMethodOverrider(context, null);
            string originalPath = httpContext.Request.Path;
            var originalRoutes = RouteTable.Routes.GetRouteData(httpContext);
            httpContext.RewritePath(nodeUrl, true);

            HttpContextBase httpContext2 = new HttpContext2(context);
            RouteData routes = mvcNode.GetRouteData(httpContext2);
            if (routes == null)
            {
                return true; // Static URL's will have no route data, therefore return true.
            }
            foreach (var routeValue in mvcNode.RouteValues)
            {
                routes.Values[routeValue.Key] = routeValue.Value;
            }
            if (originalRoutes != null && (!routes.Route.Equals(originalRoutes.Route) || originalPath != nodeUrl || mvcNode.Area == String.Empty))
            {
                routes.DataTokens.Remove("area");
                //routes.DataTokens.Remove("Namespaces");
                //routes.Values.Remove("area");
            }
            var requestContext = new RequestContext(httpContext, routes);

            // Create controller context
            var controllerContext = new ControllerContext();
            controllerContext.RequestContext = requestContext;

            // Whether controller is built by the ControllerFactory (or otherwise by Activator)
            bool factoryBuiltController = false;
            try
            {
                string controllerName = requestContext.RouteData.GetRequiredString("controller");
                controllerContext.Controller = ControllerBuilder.Current.GetControllerFactory().CreateController(requestContext, controllerName) as ControllerBase;
                factoryBuiltController = true;
            }
            catch
            {
                try
                {
                    controllerContext.Controller = Activator.CreateInstance(controllerType) as ControllerBase;
                }
                catch
                {
                }
            }

            ControllerDescriptor controllerDescriptor = null;
            if (typeof(IController).IsAssignableFrom(controllerType))
            {
                controllerDescriptor = new ReflectedControllerDescriptor(controllerType);
            }
            else if (typeof(IAsyncController).IsAssignableFrom(controllerType))
            {
                controllerDescriptor = new ReflectedAsyncControllerDescriptor(controllerType);
            }

            ActionDescriptor actionDescriptor = null;
            try
            {
                actionDescriptor = controllerDescriptor.FindAction(controllerContext, mvcNode.Action);
            }
            catch
            {
            }
            if (actionDescriptor == null)
            {
                actionDescriptor = controllerDescriptor.GetCanonicalActions().FirstOrDefault(a => a.ActionName == mvcNode.Action);
            }

            // Verify security
            try
            {
                if (actionDescriptor != null)
                {
                    // fixes #130 - Check whether we have an AllowAnonymous Attribute
                    var ignoreAuthorization = actionDescriptor.HasAttribute<AllowAnonymousAttribute>();
                    if (ignoreAuthorization)
                        return true;

                    if (actionDescriptor.HasAttribute<OnlyAnonymousAttribute>())
                        return !requestContext.HttpContext.Request.IsAuthenticated;

                    IFilterProvider filterProvider = ResolveFilterProvider();
                    IEnumerable<Filter> filters;

                    // If depencency resolver has an IFilterProvider registered, use it
                    if (filterProvider != null)
                    {
                        filters = filterProvider.GetFilters(controllerContext, actionDescriptor);
                    }
                    // Otherwise use FilterProviders.Providers
                    else
                    {
                        filters = FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor);
                    }

                    IEnumerable<AuthorizeAttribute> authorizeAttributesToCheck =
                        filters
                            .Where(f => f.Instance is AuthorizeAttribute)
                            .Select(f => f.Instance as AuthorizeAttribute);

                    // Verify all attributes
                    foreach (var authorizeAttribute in authorizeAttributesToCheck)
                    {
                        try
                        {
                            var currentAuthorizationAttributeType = authorizeAttribute.GetType();

                            var builder = new AuthorizeAttributeBuilder();
                            var subclassedAttribute =
                                currentAuthorizationAttributeType == typeof(AuthorizeAttribute) ?
                                   new InternalAuthorize(authorizeAttribute) : // No need to use Reflection.Emit when ASP.NET MVC built-in attribute is used
                                   (IAuthorizeAttribute)builder.Build(currentAuthorizationAttributeType).Invoke(null);

                            // Copy all properties
                            ObjectCopier.Copy(authorizeAttribute, subclassedAttribute);

                            if (!subclassedAttribute.IsAuthorized(controllerContext.HttpContext))
                            {
                                return false;
                            }
                        }
                        catch
                        {
                            // do not allow on exception
                            return false;
                        }
                    }
                }

                // No objection.
                return true;
            }
            finally
            {
                // Restore HttpContext
                httpContext.RewritePath(originalPath, true);

                // Release controller
                if (factoryBuiltController)
                    ControllerBuilder.Current.GetControllerFactory().ReleaseController(controllerContext.Controller);
            }
        }
        public void MapMvcAttributeRoutes_ValidatesConstraints()
        {
            // Arrange
            var controllerDescriptor = new ReflectedAsyncControllerDescriptor(typeof(InvalidConstraintController));

            string expectedMessage =
                "The constraint entry 'custom' on the route with route template 'invalidconstraint/{action}' " +
                "must have a string value or be of a type which implements 'System.Web.Routing.IRouteConstraint'.";


            // Act & Assert
            Assert.Throws<InvalidOperationException>(() => AttributeRoutingMapper.MapAttributeRoutes(controllerDescriptor), expectedMessage);
        }
 internal static IReadOnlyCollection<RouteEntry> MapAttributeRoutes(
     ReflectedAsyncControllerDescriptor controller)
 {
     SubRouteCollection collector = new SubRouteCollection();
     AddRouteEntries(collector, controller, new DefaultInlineConstraintResolver());
     return collector.Entries;
 }
        private static List<ActionDescriptor> GetActionDescriptors(ReflectedAsyncControllerDescriptor controller)
        {
            Contract.Assert(controller != null);

            AsyncActionMethodSelector actionSelector = controller.Selector;

            var actions = new List<ActionDescriptor>();
            foreach (MethodInfo method in actionSelector.ActionMethods)
            {
                string actionName = actionSelector.GetActionName(method);
                ActionDescriptorCreator creator = actionSelector.GetActionDescriptorDelegate(method);
                Debug.Assert(creator != null);

                ActionDescriptor action = creator(actionName, controller);
                actions.Add(action);
            }

            return actions;
        }
        public void MapMvcAttributeRoutes_SetsTargetIsAction()
        {
            // Arrange
            var controllerDescriptor = new ReflectedAsyncControllerDescriptor(typeof(MixedRoutingController));

            // Act
            var entries = AttributeRoutingMapper.MapAttributeRoutes(controllerDescriptor);

            // Assert
            var controllerEntry = Assert.Single(entries.Where(r => !r.Route.Defaults.ContainsKey("action")));
            Assert.False(controllerEntry.Route.GetTargetIsAction());

            var actionEntry = Assert.Single(entries.Where(r => r.Route.Defaults.ContainsKey("action")));
            Assert.True(actionEntry.Route.GetTargetIsAction());
        }
        internal static void AddRouteEntries(SubRouteCollection collector,
            ReflectedAsyncControllerDescriptor controller, IInlineConstraintResolver constraintResolver)
        {
            string prefix = GetRoutePrefix(controller);

            RouteAreaAttribute area = controller.GetAreaFrom();
            string areaName = controller.GetAreaName(area);
            string areaPrefix = area != null ? area.AreaPrefix ?? area.AreaName : null;
            ValidateAreaPrefixTemplate(areaPrefix, areaName, controller);

            AsyncActionMethodSelector actionSelector = controller.Selector;

            foreach (var method in actionSelector.DirectRouteMethods)
            {
                ActionDescriptor action = CreateActionDescriptor(controller, actionSelector, method);

                IEnumerable<IDirectRouteFactory> factories = GetRouteFactories(method, controller.ControllerType);

                AddRouteEntries(collector, areaPrefix, prefix, factories, new ActionDescriptor[] { action },
                    constraintResolver, targetIsAction: true);
            }

            // Check for controller-level routes. 
            List<ActionDescriptor> actionsWithoutRoutes = new List<ActionDescriptor>();

            foreach (var method in actionSelector.StandardRouteMethods)
            {
                ActionDescriptor action = CreateActionDescriptor(controller, actionSelector, method);

                actionsWithoutRoutes.Add(action);
            }

            IReadOnlyCollection<IDirectRouteFactory> controllerFactories = GetRouteFactories(controller);

            // If they exist and have not been overridden, create routes for controller-level route providers.
            if (controllerFactories.Count > 0 && actionsWithoutRoutes.Count > 0)
            {
                AddRouteEntries(collector, areaPrefix, prefix, controllerFactories, actionsWithoutRoutes,
                    constraintResolver, targetIsAction: false);
            }
        }
        /// <summary>
        /// Determines whether node is accessible to user.
        /// </summary>
        /// <param name="controllerTypeResolver">The controller type resolver.</param>
        /// <param name="provider">The provider.</param>
        /// <param name="context">The context.</param>
        /// <param name="node">The node.</param>
        /// <returns>
        /// 	<c>true</c> if accessible to user; otherwise, <c>false</c>.
        /// </returns>
        public bool IsAccessibleToUser(IControllerTypeResolver controllerTypeResolver, DefaultSiteMapProvider provider, HttpContext context, SiteMapNode node)
        {
            // Is security trimming enabled?
            if (!provider.SecurityTrimmingEnabled)
            {
                return true;
            }

            // Is it an external node?
            var nodeUrl = node.Url;
            if (nodeUrl.StartsWith("http") || nodeUrl.StartsWith("ftp"))
            {
                return true;
            }

            // Is it a regular node?
            var mvcNode = node as MvcSiteMapNode;
            if (mvcNode == null)
            {
                throw new AclModuleNotSupportedException(
                    Resources.Messages.AclModuleDoesNotSupportRegularSiteMapNodes);
            }

            // Clickable? Always accessible.
            if (mvcNode.Clickable == false)
            {
                return true;
            }

            // Time to delve into the AuthorizeAttribute defined on the node.
            // Let's start by getting all metadata for the controller...
            var controllerType = controllerTypeResolver.ResolveControllerType(mvcNode.Area, mvcNode.Controller);
            if (controllerType == null)
            {
                return false;
            }

            // Find routes for the sitemap node's url
            HttpContextBase httpContext = new HttpContextWrapper(context);
            string originalPath = httpContext.Request.Path;
            var originalRoutes = RouteTable.Routes.GetRouteData(httpContext);
            httpContext.RewritePath(nodeUrl, true);

            HttpContextBase httpContext2 = new HttpContext2(context);
            RouteData routes = mvcNode.GetRouteData(httpContext2);
            if (routes == null)
            {
                return true; // Static URL's will have no route data, therefore return true.
            }
            foreach (var routeValue in mvcNode.RouteValues)
            {
                routes.Values[routeValue.Key] = routeValue.Value;
            }
            if (!routes.Route.Equals(originalRoutes.Route) || originalPath != nodeUrl || mvcNode.Area == String.Empty)
            {
                routes.DataTokens.Remove("area");
                //routes.DataTokens.Remove("Namespaces");
                //routes.Values.Remove("area");
            }
            var requestContext = new RequestContext(httpContext, routes);

            // Create controller context
            var controllerContext = new ControllerContext();
            controllerContext.RequestContext = requestContext;
            try
            {
                string controllerName = requestContext.RouteData.GetRequiredString("controller");
                controllerContext.Controller = ControllerBuilder.Current.GetControllerFactory().CreateController(requestContext, controllerName) as ControllerBase;
            }
            catch
            {
                try
                {
                    controllerContext.Controller = Activator.CreateInstance(controllerType) as ControllerBase;
                }
                catch
                {
                }
            }

            ControllerDescriptor controllerDescriptor = null;
            if (typeof(IController).IsAssignableFrom(controllerType))
            {
                controllerDescriptor = new ReflectedControllerDescriptor(controllerType);
            }
            else if (typeof(IAsyncController).IsAssignableFrom(controllerType))
            {
                controllerDescriptor = new ReflectedAsyncControllerDescriptor(controllerType);
            }

            ActionDescriptor actionDescriptor = null;
            try
            {
                actionDescriptor = controllerDescriptor.FindAction(controllerContext, mvcNode.Action);
            }
            catch
            {
            }
            if (actionDescriptor == null)
            {
                actionDescriptor = controllerDescriptor.GetCanonicalActions().Where(a => a.ActionName == mvcNode.Action).FirstOrDefault();
            }

            // Verify security
            try
            {
                if (actionDescriptor != null)
                {
            #if NET35
                    IEnumerable<AuthorizeAttribute> authorizeAttributesToCheck =
                       actionDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true).OfType
                           <AuthorizeAttribute>().ToList()
                           .Union(
                               controllerDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true).OfType
                                   <AuthorizeAttribute>().ToList());
            #else
                    IEnumerable<AuthorizeAttribute> authorizeAttributesToCheck =
                        FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor)
                            .Where(f => typeof(AuthorizeAttribute).IsAssignableFrom(f.Instance.GetType()))
                            .Select(f => f.Instance as AuthorizeAttribute);
            #endif

                    // Verify all attributes
                    foreach (var authorizeAttribute in authorizeAttributesToCheck)
                    {
                        try
                        {
                            var currentAuthorizationAttributeType = authorizeAttribute.GetType();

                            var builder = new AuthorizeAttributeBuilder();
                            var subclassedAttribute =
                                currentAuthorizationAttributeType == typeof(AuthorizeAttribute) ?
                                   new InternalAuthorize(authorizeAttribute) : // No need to use Reflection.Emit when ASP.NET MVC built-in attribute is used
                                   (IAuthorizeAttribute)builder.Build(currentAuthorizationAttributeType).Invoke(null);

                            // Copy all properties
                            ObjectCopier.Copy(authorizeAttribute, subclassedAttribute);

                            if (!subclassedAttribute.IsAuthorized(controllerContext.HttpContext))
                            {
                                return false;
                            }
                        }
                        catch
                        {
                            // do not allow on exception
                            return false;
                        }
                    }
                }

                // No objection.
                return true;
            }
            finally
            {
                // Restore HttpContext
                httpContext.RewritePath(originalPath, true);
            }
        }
 private static RoutePrefixAttribute GetPrefixFrom(ReflectedAsyncControllerDescriptor controllerDescriptor)
 {
     // this only happens once per controller type, for the lifetime of the application,
     // so we do not need to cache the results
    return controllerDescriptor.GetCustomAttributes(typeof(RoutePrefixAttribute), inherit: false)
                             .Cast<RoutePrefixAttribute>().SingleOrDefault();
 }