Skip to main content

Disallow access to a folder and its children if user is anonymous

Project description

==================
iw.rejectanonymous
==================

.. contents::

What is iw.rejectanonymous ?
============================

This package is made to reject unconditionnally anonymous users from a Plone
site, without any change in your security policy matrix or workflows. They
should get redirected by plone to login form. The basic use case is an extranet,
where all visitors must be authenticated.

Works with
==========

* Plone 3
* Plone 4

Installation
============

Add ``iw.rejectanonymous`` to the ``eggs`` option of your
``plone.recipe.zope2instance`` part ::

...
[instance]
recipe = plone.recipe.zope2instance
...
eggs =
...
iw.rejectanonymous
...
...
# The ZCML slug is no more required with Plone 3.3 and up
zcml =
...
iw.rejectanonymous
...

Re-run buildout, then open the "Security" control panel of any Plone site of
your instance. A new **Private site** checkbox lets you (de)activate
``ìw.rejectanonymous``.

How to use iw.rejectanonymous ?
===============================

By default an anonymous user can browse portal:

>>> portal_url = self.portal.absolute_url()
>>> browser.open(portal_url)
>>> browser.url == portal_url
True
>>> browser.headers['status'].upper()
'200 OK'

We mark the portal with ``IPrivateSite``; this can be achieved by code or in the
ZMI using "Interfaces" tab on the portal object. Now Anonymous will get
Unauthorized exception. In a plone site this should results in a redirect to
login form.

.. admonition::
Security control panel

The Plone "Security" control panel has now a new **Private site** option for
this. A site manager does not need to go in ZMI.

Marking the site as private.

>>> from zope.interface import alsoProvides
>>> from iw.rejectanonymous import IPrivateSite
>>> alsoProvides(self.portal, IPrivateSite)
>>> browser.open(portal_url)
Traceback (most recent call last):
...
Unauthorized: ...

Login form and some styles resources are still accessible:

>>> login_form_url = self.portal.login_form.absolute_url()
>>> browser.open(login_form_url)
>>> browser.url == login_form_url
True
>>> require_login_url = self.portal.require_login.absolute_url()
>>> browser.open(require_login_url)
>>> browser.url == require_login_url
True
>>> cooked_css = self.portal.portal_css.getCookedResources()[0]
>>> cooked_css_url = '%s/portal_css/%s' % (portal_url, cooked_css.getId())
>>> browser.open(cooked_css_url)
>>> browser.url == cooked_css_url
True
>>> cooked_js = self.portal.portal_javascripts.getCookedResources()[0]
>>> cooked_js_url = '%s/portal_javascripts/%s' % (portal_url, cooked_js.getId())
>>> browser.open(cooked_js_url)
>>> browser.url == cooked_js_url
True
>>> logo_id = self.portal.base_properties.getProperty('logoName')
>>> logo_url = self.portal[logo_id].absolute_url()
>>> browser.open(logo_url)
>>> browser.url == logo_url
True
>>> mail_password_form_url = self.portal.mail_password_form.absolute_url()
>>> browser.open(mail_password_form_url)
>>> browser.url == mail_password_form_url
True

Reset password tool is accessible as well.

>>> passwordreset_url = self.portal.passwordreset.absolute_url()
>>> browser.open(passwordreset_url)
>>> browser.url == passwordreset_url
True

Then we log in, and we will be authorized to browse the portal.

>>> from Products.PloneTestCase.setup import default_user, default_password
>>> browser.open(login_form_url)
>>> browser.getControl(name='__ac_name').value = default_user
>>> browser.getControl(name='__ac_password').value = default_password
>>> browser.getControl(name="submit").click()
>>> browser.open(portal_url)
>>> browser.url == portal_url
True
>>> browser.headers['status'].upper()
'200 OK'

Customizing
===========

The default setup of authorized resources are exhaustive for a vanilla Plone
site, but some sites that are customized in depths, particularly with a
dedicated theme may need to publish other resources in the pages available to
the anonymous user, or an additional page (disclaimer, ...) linked from the
login page.

Your policy or theme component may use the ``iw.rejectanonymous.addValidIds`` to
enable new object ids to be published, and
``iw.rejectanonymous.addValidSubparts`` to add traversed resources available to
the anonymous user. The only argument required by these two functions is a
single string to add one id or subpart or a sequence of string of such ids or
subparts.

First we logout to verify this.

>>> browser.open(portal_url + '/logout')
Traceback (most recent call last):
...
Unauthorized: ...

Suppose our theme shows the ``user.gif`` and ``add_icon.gif`` icons for some
reason. The standard setup of ``iw.rejectanonymous`` does not enable this and
your page will not show as expected to the anonymous user.

>>> browser.open(portal_url + '/user.gif')
Traceback (most recent call last):
...
Unauthorized: ...
>>> browser.open(portal_url + '/add_icon.gif')
Traceback (most recent call last):
...
Unauthorized: ...

Now let's add our resources to valid ids using our customisation API.

>>> from iw.rejectanonymous import addValidIds
>>> addValidIds('user.gif', 'add_icon.gif')

And let's check the anonymous can get these resources.

>>> browser.open(portal_url + '/user.gif')
>>> browser.url
'http://nohost/plone/user.gif'
>>> browser.headers['status'].upper()
'200 OK'
>>> browser.open(portal_url + '/add_icon.gif')
>>> browser.url
'http://nohost/plone/add_icon.gif'
>>> browser.headers['status'].upper()
'200 OK'

If the custom pages available to the anonymous user require KSS, those KSS
resources cannot actually be published.

>>> cooked_kss = self.portal.portal_kss.getCookedResources()[0]
>>> cooked_kss_url = '%s/portal_kss/%s' % (portal_url, cooked_kss.getId())
>>> browser.open(cooked_kss_url)
Traceback (most recent call last):
...
Unauthorized: ...

Now let's add our subpart to the valid ones through our customisation API.

>>> from iw.rejectanonymous import addValidSubparts
>>> addValidSubparts('portal_kss')

And let's check we can now publish KSS resources to anonymous users.

>>> browser.open(cooked_kss_url)
>>> browser.url == cooked_kss_url
True
>>> browser.headers['status'].upper()
'200 OK'

Contributors
============

* Bertrand Mathieu
* Thomas Desvenain
* Gilles Lenfant

Changes log
===========

1.0.2 (2010-12-27)
------------------

- z3c.autoinclude awareness added so the ZCML slug does not need to be
explicitely added in buildout *.cfg.
[glenfant]

- Add customization utilities and doc (add new enabled ids and subpaths)
[glenfant]

- Enable favicon.
[thomasdesvenain]


1.0.1 - 2010-10-08
------------------

- Enable password reset system.
[thomasdesvenain]


1.0.0 - 2008-02-11
------------------

- Initial release
[bmathieu]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

iw.rejectanonymous-1.0.2.zip (18.7 kB view details)

Uploaded Source

File details

Details for the file iw.rejectanonymous-1.0.2.zip.

File metadata

File hashes

Hashes for iw.rejectanonymous-1.0.2.zip
Algorithm Hash digest
SHA256 4e8491587d6130a26d5c00a76cca1b97778bb41afa49ebfeaf929b8668dfc32c
MD5 be765aec6028cf49208316015c0f3a01
BLAKE2b-256 a330021a7b5081d2ad61ef217431d92ce07716992c011972b121d93a4c8f0a07

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page