Metadata-Version: 2.4
Name: splent_feature_cloudflare
Version: 0.2.0
License-Expression: LGPL-3.0-or-later
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: COPYING
License-File: COPYING.LESSER
Requires-Dist: splent_framework>=1.20.0
Dynamic: license-file

# splent_feature_cloudflare

The `cloudflare` feature of a [SPLENT](https://splent.io) product line.

A SPLENT feature is a Python package that a product composes with others.
Its models, routes, services, templates and hooks are registered when the
product starts. What this feature provides and requires is listed in the
`[tool.splent.contract]` section of `pyproject.toml`, which
`splent feature:contract --write` keeps in sync with the source.

## What it does

Cloudflare Turnstile as the product's captcha. It registers the generic
`CaptchaService`, gives templates `captcha_script()` and `captcha_widget()`,
and vetoes the comment, contact and careers submissions whose token does not
verify. The keys come from the settings stored by the panel first and from
`TURNSTILE_SITE_KEY` and `TURNSTILE_SECRET_KEY` second. Their defaults are
Cloudflare's test keys, which always pass, so development works out of the
box.

The panel edits the keys at `/admin/captcha`.

## Access

`/admin/captcha` opens only for the accounts whose role is `admin`. The
keys are site secrets and switch the captcha of every form, so staff are
refused too. Any other signed-in account gets 403 and does not see the
Captcha link in the sidebar, and an anonymous visitor is sent to log in.
The feature has no public screen of its own. Its check runs, through their
signals, inside the forms other features open to everyone, signed in or
not.

There is no setting for this role, and a refinement cannot replace the view
that checks it, because it may add routes to this feature but never replace
one of its own. Another feature can decide for this screen with an access
policy registered for the prefix `cloudflare.admin`
(`register_access_policy`, splent_framework 1.20.0). The framework asks the
policy before the role. It answers True to let an account in, False to
refuse it, or None to leave the decision to the role, and the sidebar link
follows the same answer. That is the way to let a group of accounts manage
only this section, for example the people who run the site's integrations,
without giving them the admin role.
See [Access control](https://docs.splent.io/framework/access-control).

Without such a feature, give the person's account the admin role. The staff
role does not open this screen. Staff opens every panel screen that asks
for `PANEL_ROLES`, and this one asks for `ADMIN_ROLES`.

```bash
splent feature:auth set-role someone@example.com admin
```

The stored secret key is never sent back to the browser. The settings
screen says whether one is stored and leaves its field empty. Saving with
the field empty keeps the stored secret, typing a new one replaces it, and
the remove box clears it, so the product's `TURNSTILE_SECRET_KEY` is used
again.

Two secrets leave the forms open, even when a real site key is stored. One
is an empty secret, with which the check lets every submission through, and
the other is Cloudflare's always-pass test key, the default of
`TURNSTILE_SECRET_KEY`. `CloudflareService.leaves_forms_open(secret)` is
true for both. While the secret in effect is one of them, the settings
screen warns that the captcha does not protect the forms, with a message
for each case. When a real secret is stored and the product's is empty or
the test key, the remove box says that removing it leaves the forms
unprotected, again naming the case. A live site stores its real secret
here or sets it in its configuration.

## Use it in a product

```bash
splent feature:add splent_feature_cloudflare
```

## Develop

```bash
splent feature:test splent_feature_cloudflare
splent feature:contract splent_feature_cloudflare
```

## Release

```bash
splent feature:release splent_feature_cloudflare --dry-run --bump patch
splent feature:release splent_feature_cloudflare
```

The first command rehearses the release and changes nothing. The second
publishes it to the git forge and to PyPI, and a PyPI version is permanent.
