Skip to content

Commit 433ae98

Browse files
Prepare for 2.4.2 🔱 (#603)
1 parent b2026e6 commit 433ae98

File tree

22 files changed

+2518
-110
lines changed

22 files changed

+2518
-110
lines changed

CHANGELOG.md

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,122 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.4.2] - 2025-10-04 :large_blue_diamond:
9+
10+
- Add significant improvements to authentication and authorization features.
11+
- Add built-in support for **API Key Authentication**.
12+
- Add built-in support for **Basic Authentication**.
13+
- Add built-in support for **JWT Bearer authentication** validating JWTs signed using
14+
**symmetric encryption** (previously the built-in classes only supported using
15+
asymmetric encryption to validate JWTs).
16+
- Improve the `JWTBearerAuthentication` class to support validating JWTs with both
17+
asymmetric and symmetric encryption.
18+
- Improve the code that generates OpenAPI Documentation to automatically include
19+
security `securitySchemes` and `security` sections by `Authentication` handlers
20+
configured in the application. The feature can be extended with user-defined
21+
authentication handlers.
22+
- Improve the `@auth` decorator to support specifying **sufficient roles** to
23+
authorize requests (`@auth(roles=["admin"])`).
24+
- Upgrade `GuardPost` to `1.0.3`, as it includes improved features to handle roles and
25+
JWT validation using symmetric encryption.
26+
- Upgrade `essentials` to `1.1.8` as it includes a `Secret` class to handle
27+
secrets in code. This class is used for safe handling of secrets in API Keys,
28+
Basic Credentials, and symmetric encryption for JWT Bearer authentication. It
29+
will be used in the future in all circumstances where BlackSheep code needs
30+
user-defined secrets.
31+
- Remove the code that required four env variables to be configured for the
32+
**OTLP** exporter (in the `use_open_telemetry_otlp` function), because it
33+
didn't cover legitimate use cases supported by the
34+
[**OpenTelemetry SDK**](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/).
35+
It is responsibility of the developers to configure env variables according
36+
to their preference for **OTLP**.
37+
- The framework has been tested for `cryptography>=46.0.0` and therefore update
38+
the dependency to `cryptography>=45.0.2,<47.0.0`.
39+
40+
> [!TIP]
41+
>
42+
> For a tutorial on OTLP and how it can be used with BlackSheep and an
43+
> OpenTelemetry Collector self-hosted in Kubernetes, see:
44+
> https://robertoprevato.github.io/K8sStudies/k3s/monitoring/
45+
46+
Example:
47+
48+
```python
49+
"""
50+
uvicorn apitest:app --port 44777
51+
52+
curl http://127.0.0.1:44777 -H "X-API-Key: Foo"
53+
"""
54+
55+
from dataclasses import dataclass
56+
57+
from essentials.secrets import Secret
58+
from openapidocs.v3 import Info
59+
60+
from blacksheep import Application, get
61+
from blacksheep.server.authentication.apikey import APIKey, APIKeyAuthentication
62+
from blacksheep.server.authentication.basic import BasicAuthentication, BasicCredentials
63+
from blacksheep.server.authorization import auth, allow_anonymous
64+
from blacksheep.server.openapi.v3 import OpenAPIHandler
65+
66+
app = Application()
67+
68+
69+
admin_credentials = BasicCredentials(
70+
username="admin",
71+
password=Secret("$ADMIN_PASSWORD"), # Obtained from ADMIN_PASSWORD env var
72+
roles=["admin"],
73+
)
74+
75+
print(admin_credentials.to_header_value())
76+
77+
app.use_authentication().add(
78+
APIKeyAuthentication(
79+
APIKey(
80+
secret=Secret("$API_SECRET"), # Obtained from API_SECRET env var
81+
roles=["user"],
82+
),
83+
param_name="X-API-Key",
84+
)
85+
).add(BasicAuthentication(admin_credentials))
86+
87+
app.use_authorization()
88+
89+
90+
# See the generated docs and how they include security sections
91+
docs = OpenAPIHandler(info=Info(title="Example API", version="0.0.1"))
92+
docs.bind_app(app)
93+
94+
95+
@dataclass
96+
class Foo:
97+
foo: str
98+
99+
100+
@allow_anonymous()
101+
@get("/")
102+
async def get_foo() -> Foo:
103+
return Foo("Hello!")
104+
105+
106+
@auth()
107+
@get("/claims")
108+
async def get_claims(request):
109+
return request.user.claims
110+
111+
112+
@auth(roles=["admin"], authentication_schemes=["Basic"])
113+
@get("/for-admins")
114+
async def for_admins_only(request):
115+
return request.user.claims
116+
117+
118+
if __name__ == "__main__":
119+
import uvicorn
120+
121+
uvicorn.run(app, port=44777)
122+
```
123+
8124
## [2.4.1] - 2025-09-28
9125

10126
- Correct bug in controller inheritance that would prevent argument types and
@@ -44,8 +160,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
44160
with a wrong signature ([#592](https://github.com/Neoteroi/BlackSheep/issues/592)),
45161
or that contains a bug and causes exceptions itself.
46162
Replace the Application `exception_handlers` dictionary with a user defined
47-
dictionary that validates values, and change a piece of code that causes
48-
a recursive error when an exception handler itself is buggy.
163+
dictionary that validates values, and change a piece of code that caused
164+
a recursive error when an exception handler itself was buggy.
49165
- Add support for specifying the status code in view functions
50166
([#591](https://github.com/Neoteroi/BlackSheep/issues/591)).
51167
- Fix `license` field in `pyproject.toml`.
@@ -110,7 +226,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
110226
> `2.3.0`. The breaking changes aim to improve the user experience (UX) when
111227
> using `Controllers` and registering routes. In particular, they address
112228
> issues [#511](https://github.com/Neoteroi/BlackSheep/issues/511) and
113-
> [#540](https://github.com/Neoteroi/BlackSheep/issues/540).The scope of the
229+
> [#540](https://github.com/Neoteroi/BlackSheep/issues/540). The scope of the
114230
> breaking changes is relatively minor, as they affect built-in features that
115231
> are *likely* not commonly modified: removes the `prepare_controllers` and the
116232
> `get_controller_handler_pattern` from the `Application` class, transferring

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,15 +219,21 @@ async def only_for_authenticated_users():
219219

220220
BlackSheep provides:
221221

222-
* [Built-in support for OpenID Connect authentication](https://www.neoteroi.dev/blacksheep/authentication/#oidc)
223-
* [Built-in support for JWT Bearer authentication](https://www.neoteroi.dev/blacksheep/authentication/#jwt-bearer)
222+
* [Built-in support for **OpenID Connect** authentication](https://www.neoteroi.dev/blacksheep/authentication/#oidc)
223+
* [Built-in support for **JWT Bearer** authentication](https://www.neoteroi.dev/blacksheep/authentication/#jwt-bearer)
224224

225225
Meaning that it is easy to integrate with services such as:
226226
* [Auth0](https://auth0.com)
227227
* [Azure Active Directory](https://azure.microsoft.com/en-us/services/active-directory/)
228228
* [Azure Active Directory B2C](https://docs.microsoft.com/en-us/azure/active-directory-b2c/overview)
229229
* [Okta](https://www.okta.com)
230230

231+
Since version `2.4.2`, it also offers built-in support for **Basic authentication**,
232+
**API Key authentication**, **JWT Bearer authentication using symmetric encryption**,
233+
and automatic generation of OpenAPI Documentation for security schemes when using
234+
built-in classes for authentication. It supports defining custom authentication handlers
235+
and custom mappers for OpenAPI Documentation.
236+
231237
Refer to the documentation and to [BlackSheep-Examples](https://github.com/Neoteroi/BlackSheep-Examples)
232238
for more details and examples.
233239

blacksheep/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55

66
__author__ = "Roberto Prevato <[email protected]>"
7-
__version__ = "2.4.1"
7+
__version__ = "2.4.2"
88

99
from .contents import Content as Content
1010
from .contents import FormContent as FormContent

blacksheep/messages.pyx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ cdef class Message:
167167

168168
async def read(self):
169169
if self.content:
170-
# TODO: return content.body if not an instance of StreamedContent?
171170
return await self.content.read()
172171
return None
173172

blacksheep/normalization.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ def copy_special_attributes(source_method, wrapper) -> None:
22
for name in {
33
"auth",
44
"auth_policy",
5+
"auth_roles",
56
"auth_schemes",
67
"allow_anonymous",
78
"controller_type",

blacksheep/server/application.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,14 @@ def __init__(
223223
if env_settings.add_signal_handler:
224224
use_shutdown_handler(self)
225225

226+
@property
227+
def authentication_strategy(self) -> Optional[AuthenticationStrategy]:
228+
return self._authentication_strategy
229+
230+
@property
231+
def authorization_strategy(self) -> Optional[AuthorizationStrategy]:
232+
return self._authorization_strategy
233+
226234
@property
227235
def controllers_router(self) -> RoutesRegistry:
228236
return self.router.controllers_routes
@@ -476,7 +484,7 @@ def use_authorization(
476484
# meaning that request handlers allow anonymous users by default, unless
477485
# they are decorated with @auth()
478486
strategy.default_policy = Policy("default")
479-
strategy.add(Policy("authenticated").add(AuthenticatedRequirement()))
487+
strategy.add(Policy("authenticated", AuthenticatedRequirement()))
480488

481489
self._authorization_strategy = strategy
482490
self.exceptions_handlers.update(

0 commit comments

Comments
 (0)