Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ jobs:
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
submodules: false

- name: Use Python ${{ matrix.python-version }}
Expand Down
7 changes: 0 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@
This repository contains the source code of the documentation that gets
published to [https://www.neoteroi.dev/](https://www.neoteroi.dev/).

---

Work in progress. 🚧
The code has been modified to unify different projects.

---

## How to contribute

The documentation uses MkDocs and Material for MkDocs. For information on how
Expand Down
74 changes: 43 additions & 31 deletions blacksheep/docs/anti-request-forgery.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@ requests.
Examples of such situations are:

- Cookies are automatically included in web requests, so if an application uses
cookie-based authentication, credentials are sent automatically
cookie-based authentication, credentials are sent automatically.
- After a user signs in with Basic or Digest authentication, the browser
automatically sends the credentials until the session ends
automatically sends the credentials until the session ends.

If a web application uses cookie-based authentication or other features that
cause credentials to be automatically included in web requests, it requires
anti-forgery measures.

BlackSheep implements built-in support for anti-request-forgery validation, this
page describes how to use the built-in solution.
page describes how to use it.

!!! tip
Applications that store access tokens (for example JWTs) in the HTML5
storage and include them in `Authorization: Bearer {...}` headers, are not
vulnerable to CSRF and do not require anti-forgery measures.
/// admonition | Options using HTML5 Storage.
type: tip

Applications that store access tokens (for example JWTs) in the HTML5
storage and include them in `Authorization: Bearer {...}` headers, are not
vulnerable to CSRF and do not require anti-forgery measures.

///

## How to use the built-in anti-forgery validation

Expand All @@ -37,10 +41,10 @@ app = Application(show_error_details=True)
use_anti_forgery(app)
```

The call to `use_anti_forgery(app)` configures a middleware that can issue and
validate anti-forgery tokens, and extensions for Jinja2 templates to render
anti-forgery tokens in HTML templates. It is important to configure templating
before anti-forgery because the latter configures the extensions on the Jinja2
The `use_anti_forgery(app)` function configures middleware to issue and
validate anti-forgery tokens, as well as extensions for Jinja2 templates to
render these tokens in HTML templates. It is important to configure templating
before enabling anti-forgery, as the latter sets up extensions in the Jinja2
environment.

Consider an example having this folder structure:
Expand All @@ -61,7 +65,7 @@ from blacksheep import Application, FromForm, get, post, view
from blacksheep.server.csrf import use_anti_forgery


app = Application(show_error_details=True)
app = Application()

use_anti_forgery(app)

Expand Down Expand Up @@ -101,19 +105,23 @@ And `index.jinja` contains the following template:
</html>
```

The `{% af_input %}` custom tag is used to render an HTML input element containing an
anti-forgery token. The built-in solution uses the Double-Token strategy: when
an anti-forgery token is required to render HTML for a response, a corresponding
HTTP-only cookie is configured for the response. The value of the cookie and the
control parameter are matched in following requests for validation. Contextually,
response headers are also set to protect the HTML view against click-jacking and to
forbid iframes.
The `{% af_input %}` tag renders an HTML input element containing an
anti-forgery token. The built-in solution uses the Double-Token strategy. When
an anti-forgery token is required to render HTML, a corresponding HTTP-only
cookie is included in the response. The cookie's value and the control
parameter are matched in subsequent requests for validation. Contextually,
response headers are also set to protect the HTML view against click-jacking
and to forbid iframes.

/// admonition | Alternative tags.
type: tip

!!! tip "Alternative tags"
In alternative to `{% af_input %}`, it is possible to use the tag
`{% csrf_input %}` (like Django). However, `af_input` is recommended since
the objective of the tag is to obtain an input element containing an
anti-forgery token, not to achieve Cross-Site Request Forgery!
In alternative to `{% af_input %}`, it is possible to use the tag
`{% csrf_input %}` (like Django). However, `af_input` is recommended since
the objective of the tag is to obtain an input element containing an
anti-forgery token, not to achieve Cross-Site Request Forgery!

///

An example of a rendered view looks like the following:

Expand All @@ -138,14 +146,18 @@ Validation is applied by default to all `DELETE PATCH POST PUT` web requests.
Requests using other methods are not validated as they are not supposed to
change the state and should execute read-only operations.

!!! danger "Important note about token generation"
Tokens are signed using symmetric encryption. For your production
environments, configure application secrets using environment variables
as described in [data protection](../dataprotection/).
/// admonition | Important note about token generation.
type: danger

Tokens are signed using symmetric encryption. For your production environments,
configure application secrets using environment variables as described in [data
protection](dataprotection.md).

///

## How to send the anti-forgery token

The anti-forgery token can be sent to the server in one of these ways:
The anti-forgery token can be sent to the server in one of the following ways:

| Location | Parameter Name |
| -------------- | ---------------------------- |
Expand Down Expand Up @@ -226,8 +238,8 @@ async def create_example():

## Custom AntiForgeryHandler classes

The following example shows how to override methods of the `AntiForgeryHandler`
class:
The following example demonstrates how to override methods of the
`AntiForgeryHandler`:

```python
from blacksheep.server.csrf import AntiForgeryHandler, use_anti_forgery
Expand Down
109 changes: 55 additions & 54 deletions blacksheep/docs/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,22 @@ trace, serving a page like the following:

![Internal server error page](./img/internal-server-error-page.png)

Consider using environmental variables to handle these kinds of settings that
can vary across environments. For example:
/// admonition | Use the `APP_SHOW_ERROR_DETAILS`.
type: tip

```python
import os
from blacksheep import Application

app = Application(show_error_details=bool(os.environ.get("SHOW_ERROR_DETAILS", None)))


@get("/")
def crash_test():
raise Exception("Crash test")
Rather than using the `show_error_details` parameter, it is recommended to use
the environment variable `APP_SHOW_ERROR_DETAILS` to control whether the
application displays detailed error information. Setting
`APP_SHOW_ERROR_DETAILS=1` or `APP_SHOW_ERROR_DETAILS=True` enables this
feature.
///

```
/// admonition | Settings strategy

!!! info "Settings strategy"
BlackSheep project templates include a strategy to handle application
settings and configuration roots.
BlackSheep project templates include a strategy to handle application
settings and configuration roots. Refer to [_Getting started with the MVC project template_](./mvc-project-template.md)
for more information.
///

### Configuring exceptions handlers

Expand All @@ -69,14 +66,13 @@ handling a web request and reaches the application, the application checks if
there is a matching handler for that kind of exception. An exception handler is
defined as a function with the following signature:

```python
```python {hl_lines="3"}
from blacksheep import Request, Response, text

async def exception_handler(self, request: Request, exc: Exception) -> Response:
pass
```

In the exception below
```python

class CustomException(Exception):
Expand Down Expand Up @@ -114,7 +110,7 @@ specific handler for one of the descendant classes.
It is also possible to register exception handlers using decorators, instead
of interacting with `app.exceptions_handlers` dictionary:

```python
```python {hl_lines="5"}
class CustomException(Exception):
pass

Expand All @@ -131,8 +127,7 @@ To override how unhandled exceptions are handled, define a custom `Application`
class overriding its `handle_internal_server_error` method, like in the
following example:

```python

```python {hl_lines="5-6"}
from blacksheep import Application, json
from blacksheep.messages import Request

Expand Down Expand Up @@ -165,7 +160,7 @@ create an HTTP `ClientSession` that will be disposed of when the application
stops. Note how the instance of `ClientSession` is also bound to application
services, so that it can be injected into request handlers that need it.

```python
```python {linenums="1" hl_lines="9-10 16"}
import asyncio
from blacksheep import Application
from blacksheep.client.pool import ClientConnectionPools
Expand Down Expand Up @@ -198,14 +193,22 @@ if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=44777, log_level="debug", lifespan="on")
```

!!! info
The method leverages `contextlib.asynccontextmanager`. What is defined
before the `yield` statement executes when the application starts, and what
is defined after the `yield` statement executes when the application stops.
- The code before the `yield` statement (lines _11-16_) is executed when the
application starts.
- The code after the `yield` statement (lines _17-18_) is executed when the
application stops.

/// admonition | @app.lifespan

This method leverages `contextlib.asynccontextmanager`. What is defined
before the `yield` statement executes when the application starts, and what
is defined after the `yield` statement executes when the application stops.

///

The following example illustrates how a `redis-py` [connection can be disposed
of](https://redis.readthedocs.io/en/stable/examples/asyncio_examples.html)
using the same method:
using `@app.lifespan`:

```python
import redis.asyncio as redis
Expand All @@ -231,31 +234,39 @@ async def configure_redis():
await connection.close()
```

!!! info "Example using Redis"
The `BlackSheep-Examples` repository includes an example where `Redis` is
used to store access tokens and refresh tokens obtained using
`OpenID Connect`: [example](https://github.com/Neoteroi/BlackSheep-Examples/blob/main/oidc/scopes_redis_aad.py). For more information on `redis-py` and its async
interface, refer to its [official documentation](https://redis.readthedocs.io/en/stable/examples/asyncio_examples.html).

### on_start

This event should be used to configure things such as new request handlers,
and services registered in `app.services`, such as database connection pools,
This event should be used to configure components such as new request handlers
and services registered in `app.services`, including database connection pools
and HTTP client sessions.

### after_start

This event should be used to configure things that must happen after request
handlers are normalized. At this point, the application router contains information
about actual routes handled by the web application, and routes can be inspected.
For example, the built-in generation of OpenAPI Documentation generates the
API specification file at this point.
This event should be used to configure tasks that must occur after request
handlers are normalized. At this stage, the application router contains
information about the actual routes handled by the web application, allowing
routes to be inspected. For example, the built-in OpenAPI documentation
generation creates the API specification file at this point.

/// admonition | Example: inspecting routes.
type: tip

An `after_start` callback that prints all routes registered in the application
router:

```python
@app.after_start
async def after_start_print_routes(application: Application) -> None:
print(application.router.routes)
```
///

### on_stop

This event should be used to fire callbacks that need to happen when the application
is stopped. For example, disposing of services that require disposal, such as
database connection pools, and HTTP client sessions using connection pools.
This event should be used to trigger callbacks that need to run when the
application stops. For example, it can be used to dispose of services that
require cleanup, such as database connection pools and HTTP client sessions
using connection pools.

### Application life cycle

Expand Down Expand Up @@ -329,16 +340,6 @@ are fired, and the state of the application when they are executed.
app.on_stop += on_stop
```

!!! info
For example, to define an `after_start` callback that logs all routes registered
in the application router:

```python
@app.after_start
async def after_start_print_routes(application: Application) -> None:
print(application.router.routes)
```

## Next

Read about the details of [routing in BlackSheep](../routing).
Read about the details of [routing in BlackSheep](routing.md).
17 changes: 10 additions & 7 deletions blacksheep/docs/asgi.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# ASGI Servers

BlackSheep belongs to the category of
[ASGI](https://asgi.readthedocs.io/en/latest/) web frameworks, so it requires
an ASGI HTTP server to run, such as [uvicorn](http://www.uvicorn.org/), or
[hypercorn](https://pgjones.gitlab.io/hypercorn/). All examples in this
documentation use `Uvicorn`, but the framework has been tested also with
`Hypercorn` and should work with any server that implements ASGI.
BlackSheep is an [ASGI](https://asgi.readthedocs.io/en/latest/) web framework,
which requires an ASGI HTTP server to run, such as
[Uvicorn](http://www.uvicorn.org/), or
[Hypercorn](https://pgjones.gitlab.io/hypercorn/). All examples in this
documentation use `Uvicorn`, but the framework has also been tested with
Hypercorn and should work with any server that implements the `ASGI`
specification.

### Uvicorn

Expand All @@ -20,8 +21,10 @@ documentation use `Uvicorn`, but the framework has been tested also with
<br />
<div class="img-auto-width"></div>
<p align="left">
<a href="https://pgjones.gitlab.io/hypercorn/"><img width="270" src="https://pgjones.gitlab.io/hypercorn/_images/logo.png" alt="Hypercorn"></a>
<a href="https://github.com/pgjones/hypercorn"><img width="270" src="https://raw.githubusercontent.com/pgjones/hypercorn/main/artwork/logo.png" alt="Hypercorn"></a>
</p>

---

Many details, such as how to run the server in production, depend on the chosen
ASGI server.
Loading