Anonymous no access to front end- but want to show a splash screen before login

Topics: Customizing Orchard, General
Aug 6, 2015 at 5:57 PM
what i am looking for is the ability to allow some pages accessible to anonymous users that do not have privilege to the "front-end" as defined in the user roles permissions.

I thought at first this was a simple matter of giving permissions to the content item VIA contentPermissions but this only worked if view content and "access to front end" was checked. But it seemed in this case it didn't matter, he just had access to everything in the site then.

How can I create a /publicPages/ route that gives a user access to any route under publicpages and also not allow the anonymous user to access any other part of the site without logging in.

thanks!
Aug 7, 2015 at 12:13 AM
Edited Aug 7, 2015 at 12:16 AM
Okay, Well this is how I went about tackling this. This solution allows my CMS content providers to create public pages while maintaining that the anonymous user does not have access to the "front-end".

When creating the page content item they specify the route as /page/{The page name}

I created a controller that gets the content item by route, generate a shape and display that.

Here is the very simple code.
    [Themed]
    [ValidateInput(false)]
    public class PublicPageController : Controller
    {
        private readonly IOrchardServices _orchardServices;
        private readonly IContentManager _contentManager;
        private readonly IHttpContextAccessor _hca;

        public ILogger Logger { get; set; }

        private dynamic Shape { get; set; }

        public Localizer T { get; set; }

        public PublicPageController(IOrchardServices orchardServices,
                                    IHttpContextAccessor hca,
                                    IContentManager contentManager,
                                    IShapeFactory shapeFactory
                        )
        {
            _contentManager = contentManager;
            _orchardServices = orchardServices;
            _hca = hca;
            Logger = NullLogger.Instance;
            Shape = shapeFactory;
        }

        [AlwaysAccessible]
        public ActionResult Public(string page)
        {
            var contentItem = _orchardServices.ContentManager.Query<AutoroutePart, AutoroutePartRecord>().Where(x => x.DisplayAlias == _hca.Current().Request.Path.Substring(1)).List().FirstOrDefault();
            return new ShapeResult(this, _contentManager.BuildDisplay(contentItem));
        }
    }


Routes.cs

        public IEnumerable<RouteDescriptor> GetRoutes()
        {
            return new[] {
                            new RouteDescriptor {
                                                    Priority = 91,
                                                    Route = new Route(
                                                         "public/{page}",
                                                         new RouteValueDictionary {
                                                                                      {"area", "yourArea"},
                                                                                      {"controller", "PublicPage"},
                                                                                      {"action", "Public"}
                                                                                  },
                                                         new RouteValueDictionary(),
                                                         new RouteValueDictionary {
                                                                                      {"area", "yourArea"}
                                                                                  },
                                                         new MvcRouteHandler())
                                                 }
                         };
        }

with an empty view called page
Aug 7, 2015 at 1:56 AM
So, after getting the splash screen accessible to the outside world without having to be logged in, I am now in position to redirect an anonymous user to the splash screen.

Finding some information on IUserEventHandler, I created my own but have been unable to trigger AccessDenied when I am logged out. It seems an anonymous user doesn't trigger IUserEventHandler?

I am able to see events fired in my IUserEventHandler once I log in or attempted log-in. Just not AccessDenied on an anonymous user.

Is this default behavior? am i missing something? Are there alternative ways to trigger a URL redirect for users that are not authenticated?

One immediate approach in my mind is a authorization filter validating authentication and redirecting if failed, but that seems really brute force-ish.

Another method I though, and people seem to be discussing are workflows. I just can't exactly figure out how to add an activity that is accessDenied.
Aug 7, 2015 at 3:19 AM
User workflows do not trigger AccessDenied for unauthenticated users either.
Aug 7, 2015 at 4:07 AM
Edited Aug 7, 2015 at 4:08 AM
So found out a way, right way? dunno, to do it..

Create an IAuthenticationFilter, suppress orchards and make it go where you want.
    [OrchardSuppressDependency("Orchard.Users.Services.AuthenticationRedirectionFilter")]
    public class AuthenticationRedirectionFilter : FilterProvider, IAuthenticationFilter
    {
        private readonly IHttpContextAccessor _httpContext;

        public AuthenticationRedirectionFilter(IHttpContextAccessor httpContext)
        {
            _httpContext = httpContext;
        }

        public void OnAuthentication(AuthenticationContext filterContext)
        {
        }

        public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
        {
            if (filterContext.Result is HttpUnauthorizedResult)
            {
                filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;

                if (filterContext.HttpContext.Request.RawUrl.Length > 1)
                {
                    // deep link
                    filterContext.Result = new RedirectToRouteResult(
                        new RouteValueDictionary
                {
                    { "controller", "Account" },
                    { "action", "AccessDenied" },
                    { "area", "Orchard.Users"},
                    { "ReturnUrl", filterContext.HttpContext.Request.RawUrl }
                });
                }
                else
                {
                    filterContext.Result = new RedirectResult("~/public/about");
                }
            }
        }
    }
I ignore deep linking and allow those to go directly to login page.