processwire / processwire-requests

ProcessWire feature requests.
39 stars 0 forks source link

Allow restricting access for the entire site, starting from the home / root page #376

Open teppokoivula opened 3 years ago

teppokoivula commented 3 years ago

Short description of the enhancement

I would like to be able to protect entire site from non-authenticated access, hence I'd like to disable guest view access for the home page.

Current vs. suggested behavior

Currently it's not possible to remove guest access to home page. My suggestion would be to either...

a) remove this restriction and instead add a proper warning, b) allow superusers (via admin) to decide if this should be doable, or c) add a way to specify via site config or custom hook whether this should be possible.

As things are now, this needs to be done with custom code, which is somewhat prone to error: unless it's done with hooks the developer may accidentally allow public access to things like assets, and even with the hook approach it can get complicated. Other hooks (i.e. from an installed module) may also accidentally interfere with this sort of protection.

Another option is to leave the home page unprotected, which in turn can result in an unnecessarily complex page structure, and requires making extra sure that the home page can't accidentally contain any non-public content and that all the pages that can be added as direct children are access protected.

Why would the enhancement be useful to users?

This is primarily intended for sites that are completely access-protected, such as intranets, extranets, internal tools or other types of non-public portals, sites still under development, and overall any situation where the site intentionally provides no public interface at all (not counting the login page).

Overall I believe that this feature would add flexibility and make a thing that now requires going through some hoops easier. The downsides are, in my opinion, less of an issue: if superuser and/or developer access is required anyway, said superuser/developer should be able to reverse the protection if it turns out to be a problem.

Optional: Screenshots/Links that demonstrate the enhancement

Some related links — might update this list later, just pointing out that this has indeed been discussed over the years (I'm probably missing a few cases here):

JanRomero commented 2 months ago

Just to get some momentum on this 17-upvotes issue and having only read the OP’s first link… Is this mostly a problem of unaligned visions or are there deep technical issues that would require big overhauls of the permission system to implement this safely?

I could see the former being addressed by a config option. We already have dangerous superuser settings hidden behind $config->advanced, why not this, too? It would definitely reduce the danger of someone just blindly confirming changes.

ryancramerdesign commented 2 months ago

@JanRomero @teppokoivula The way I do it is this in my /site/templates/_init.php file:

if($user->isGuest()) die("Public access disabled"); 

or this

if($user->isGuest()) return $this->halt("Pubic access disabled"); 

Another simple option is .htaccess basic authentication. Since it's easy to do these things (like above, or with a module), I'm not sure about this being in the core. Doing the above seems more flexible, since it gives you full control over the conditions and output. For instance, maybe you want to allow the request through if a particular GET variable is present like client_preview=1. Or maybe you want it output something more elaborate, or do a 404, 403, etc.

teppokoivula commented 2 months ago

Hey Ryan!

Thanks for the response. A few quick comments:

The way I do it is this in my /site/templates/_init.php file:

This is more or less the same I've been doing. That being said, It doesn't automatically take care of secure files, and I'm always afraid that there's some other way to circumvent the protection (though not sure if that's really the case). Anyway, for assets alone additional code (hooks) are requires, or alternatively the whole site needs to be structured "one level deeper", which feels a bit awkward.

Additionally -- and I'll readily admit that this is not so much a real issue but rather a "best practice thing" -- I've been taught (and keep telling others) that access restriction and authentication are both things that one should avoid doing themselves, whenever possible, rather relying on existing, well tested solutions. Having to protect part of the site in code (however easy it looks like at first glance) is at least dangerously close to rolling your own access protection 🙂

Another simple option is .htaccess basic authentication.

To be fair this is a different thing altogether. Assuming that there are many user accounts, and potentially some behaviour added on top of native authentication (e.g. direct LDAPS auth or OAuth), replicating all that in htaccess basic auth is not necessarily feasible. At the very least it would be pretty complicated to pull off.

Since it's easy to do these things (like above, or with a module), I'm not sure about this being in the core. Doing the above seems more flexible, since it gives you full control over the conditions and output. For instance, maybe you want to allow the request through if a particular GET variable is present like client_preview=1. Or maybe you want it output something more elaborate, or do a 404, 403, etc.

While I appreciate the idea that handling this on a case by case in code provides added flexibility, I must say that, in my experience so far, things like providing easy way to circumvent authentication are relatively rare compared to the need to simply protect the whole site.

I tried to collect some threads in the issue where users have brought this need up, and I'm confident that this is just a subset of all the cases the need has come up. I personally have needed it quite often, and I'd really like to have a built-in core way to do it instead of rolling my own logic (that may or may not be as solid as ProcessWire's native permission handling, and may or may not cover all the things the core mechanism does.)

Anyway, I hope that you would reconsider this. Personally I still don't see a truly solid reason not to add this as an option, assuming that it's mostly just about allowing it and doesn't require major rewrite of the permission system, or something along those lines. If I'm wrong and it is indeed something that would require a major rewrite, that definitely changes the situation -- quite a bit 🙂

ryancramerdesign commented 2 months ago

@teppokoivula It sounds like your use case might be entirely different from what I'm thinking. The times I've needed this were to launch a new site that I didn't want random people or Google to start clicking through until the client said "go". And what I described was ideal for the use case I had. But it sounds like your case is something entirely different, more of a permanent access control situation (?).

I'm having trouble picturing an installation without a homepage as the gateway, at least having a "go away" message or login form or something. Where does an authorized user go? The more I can understand the use case, I think the better I can code a way to make it possible. I think I could support it once I get it.

Methods like these are definitely part of PW's access control API. They aren't rolling your own access control, they are using the tools that PW provides for access control and they are meant to be used. They are very reliable and dependable.

$user->isGuest();
$user->isLoggedin();
$user->hasRole('role');
$user->hasPermission('permission'); 
and so on...

With regard to access control of files, if enabled in your config, those are controlled by template, and not by any function/method. So if you need that for the homepage, that's true, I don't think you can access control direct file URLs on the homepage. Likely possible to support though, I'll definitely look into it.

teppokoivula commented 2 months ago

It sounds like your use case might be entirely different from what I'm thinking. The times I've needed this were to launch a new site that I didn't want random people or Google to start clicking through until the client said "go". And what I described was ideal for the use case I had. But it sounds like your case is something entirely different, more of a permanent access control situation (?).

Exactly. I've used (and been happy with) similar strategies for handling cases where it's more about keeping public out of a site while it's been worked on, but this is a different case entirely :)

What I'm talking about here are sites with no public front-end whatsoever, or even no front-end whatsoever. Basically intranet or extranet sites (this is the most common use case for me), or alternatively sites that act as some sort of an application. One example of that latter category are the portals we use to manage various services internally; they are strictly behind lock and key, and no part of the front-end should ever be visible without authentication.

Methods like these are definitely part of PW's access control API. They aren't rolling your own access control, they are using the tools that PW provides for access control and they are meant to be used. They are very reliable and dependable.

I'm seeing now that my message did not come through quite as intended, sorry for that. What I meant was that I don't like having to check access/permissions in code, at any level, unless I absolutely have to.

I know that it's technically entirely viable, but there's always a chance of human error: what if I, for an example, add something new to said code or perhaps want to temporarily disable it on specific conditions, but mess it up? Boom, secrets leak and I'm in deep, deep trouble.

Template level access restriction works on a deeper level, so there's (in my opinion) less chance of messing things up. And more of a guarantee that the file containing aforementioned isLoggedin() or hasRole() check doesn't accidentally get left out, for whatever reason. And so on.

And yes, it's also about ease of use: I just want to protect the whole site. If it was any other page, apart from the home page, I can flip one switch and that's all I will ever need :)

Where does an authorized user go?

ProcessWire's login page. For many cases that would be the perfect solution. Or, unless that's somehow not possible here, the built-in option to render page by ID could be used here to render a separate login page; I don't use this option much, but it has come in handy a few times.

In some cases this could also be where a separate module take's over and redirects the user somewhere else, e.g. SAML/LDAPS/OAuth service somewhere.

I've done quite a few "fully access restricted" sites with ProcessWire, and it has always been a bit of a hassle. Again, I get that it's not a major issue and technically we can do it in code, but I'd be so much happier if it didn't require a single line of code, fully eliminating that human error aspect, and also automatically bringing in benefits such as secured assets.