refactor: apply ruff migrations for python 3.10+

This commit is contained in:
Éloi Rivard 2024-10-28 09:13:00 +01:00
parent 37adb66e06
commit 0b51b01031
No known key found for this signature in database
GPG key ID: 7EDA204EA57DD184
10 changed files with 121 additions and 143 deletions

View file

@ -2,7 +2,6 @@ import os
import smtplib
import socket
import sys
from typing import Optional
from flask import current_app
from pydantic import ValidationError
@ -51,7 +50,7 @@ class RootSettings(BaseSettings):
You MUST change this.
"""
SERVER_NAME: Optional[str] = None
SERVER_NAME: str | None = None
"""The Flask :external:py:data:`SERVER_NAME` configuration setting.
This sets domain name on which canaille will be served.
@ -87,21 +86,21 @@ def settings_factory(config, env_file=".env", env_prefix=""):
):
from canaille.backends.sql.configuration import SQLSettings
attributes["CANAILLE_SQL"] = (Optional[SQLSettings], None)
attributes["CANAILLE_SQL"] = ((SQLSettings | None), None)
if "CANAILLE_LDAP" in config or any(
var.startswith("CANAILLE__LDAP__") for var in os.environ
):
from canaille.backends.ldap.configuration import LDAPSettings
attributes["CANAILLE_LDAP"] = (Optional[LDAPSettings], None)
attributes["CANAILLE_LDAP"] = ((LDAPSettings | None), None)
if "CANAILLE_OIDC" in config or any(
var.startswith("CANAILLE_OIDC__") for var in os.environ
):
from canaille.oidc.configuration import OIDCSettings
attributes["CANAILLE_OIDC"] = (Optional[OIDCSettings], None)
attributes["CANAILLE_OIDC"] = ((OIDCSettings | None), None)
Settings = create_model(
"Settings",

View file

@ -2,7 +2,6 @@ import copy
import datetime
import uuid
from typing import Any
from typing import Dict
from canaille.backends import Backend
@ -12,7 +11,7 @@ def listify(value):
class MemoryBackend(Backend):
indexes: Dict[str, Dict[str, Any]] = None
indexes: dict[str, dict[str, Any]] = None
"""Associates ids and states."""
attribute_indexes = None

View file

@ -4,8 +4,6 @@ import typing
from collections import ChainMap
from typing import Annotated
from typing import ClassVar
from typing import List
from typing import Optional
from typing import get_origin
from typing import get_type_hints
@ -20,7 +18,7 @@ class Model:
It details all the common attributes shared by every models.
"""
id: Optional[str] = None
id: str | None = None
"""A unique identifier for a SCIM resource as defined by the service
provider. Id will be :py:data:`None` until the
:meth:`~canaille.backends.models.BackendModel.save` method is called.
@ -38,11 +36,11 @@ class Model:
Section 9 for additional considerations regarding privacy.
"""
created: Optional[datetime.datetime] = None
created: datetime.datetime | None = None
"""The :class:`~datetime.datetime` that the resource was added to the
service provider."""
last_modified: Optional[datetime.datetime] = None
last_modified: datetime.datetime | None = None
"""The most recent :class:`~datetime.datetime` that the details of this
resource were updated at the service provider.
@ -50,7 +48,7 @@ class Model:
the value MUST be the same as the value of :attr:`~canaille.backends.models.Model.created`.
"""
_attributes: ClassVar[Optional[List[str]]] = None
_attributes: ClassVar[list[str] | None] = None
@classproperty
def attributes(cls):

View file

@ -1,7 +1,6 @@
import datetime
import typing
import uuid
from typing import List
from sqlalchemy import Boolean
from sqlalchemy import Column
@ -82,8 +81,8 @@ class User(canaille.core.models.User, Base, SqlAlchemyModel):
given_name: Mapped[str] = mapped_column(String, nullable=True)
formatted_name: Mapped[str] = mapped_column(String, nullable=True)
display_name: Mapped[str] = mapped_column(String, nullable=True)
emails: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
phone_numbers: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
emails: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
phone_numbers: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
formatted_address: Mapped[str] = mapped_column(String, nullable=True)
street: Mapped[str] = mapped_column(String, nullable=True)
postal_code: Mapped[str] = mapped_column(String, nullable=True)
@ -95,7 +94,7 @@ class User(canaille.core.models.User, Base, SqlAlchemyModel):
department: Mapped[str] = mapped_column(String, nullable=True)
title: Mapped[str] = mapped_column(String, nullable=True)
organization: Mapped[str] = mapped_column(String, nullable=True)
groups: Mapped[List["Group"]] = relationship(
groups: Mapped[list["Group"]] = relationship(
secondary=membership_association_table, back_populates="members"
)
lock_date: Mapped[datetime.datetime] = mapped_column(
@ -118,7 +117,7 @@ class Group(canaille.core.models.Group, Base, SqlAlchemyModel):
display_name: Mapped[str] = mapped_column(String)
description: Mapped[str] = mapped_column(String, nullable=True)
members: Mapped[List["User"]] = relationship(
members: Mapped[list["User"]] = relationship(
secondary=membership_association_table, back_populates="groups"
)
@ -146,10 +145,10 @@ class Client(canaille.oidc.models.Client, Base, SqlAlchemyModel):
description: Mapped[str] = mapped_column(String, nullable=True)
preconsent: Mapped[bool] = mapped_column(Boolean, nullable=True)
post_logout_redirect_uris: Mapped[List[str]] = mapped_column(
post_logout_redirect_uris: Mapped[list[str]] = mapped_column(
MutableJson, nullable=True
)
audience: Mapped[List["Client"]] = relationship(
audience: Mapped[list["Client"]] = relationship(
"Client",
secondary=client_audience_association_table,
primaryjoin=id == client_audience_association_table.c.client_id,
@ -164,13 +163,13 @@ class Client(canaille.oidc.models.Client, Base, SqlAlchemyModel):
TZDateTime(timezone=True), nullable=True
)
client_name: Mapped[str] = mapped_column(String, nullable=True)
contacts: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
contacts: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
client_uri: Mapped[str] = mapped_column(String, nullable=True)
redirect_uris: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
redirect_uris: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
logo_uri: Mapped[str] = mapped_column(String, nullable=True)
grant_types: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
response_types: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
scope: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
grant_types: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
response_types: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
scope: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
tos_uri: Mapped[str] = mapped_column(String, nullable=True)
policy_uri: Mapped[str] = mapped_column(String, nullable=True)
jwks_uri: Mapped[str] = mapped_column(String, nullable=True)
@ -201,7 +200,7 @@ class AuthorizationCode(canaille.oidc.models.AuthorizationCode, Base, SqlAlchemy
subject: Mapped["User"] = relationship()
redirect_uri: Mapped[str] = mapped_column(String, nullable=True)
response_type: Mapped[str] = mapped_column(String, nullable=True)
scope: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
scope: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
nonce: Mapped[str] = mapped_column(String, nullable=True)
issue_date: Mapped[datetime.datetime] = mapped_column(
TZDateTime(timezone=True), nullable=True
@ -243,7 +242,7 @@ class Token(canaille.oidc.models.Token, Base, SqlAlchemyModel):
subject: Mapped["User"] = relationship()
type: Mapped[str] = mapped_column(String, nullable=True)
refresh_token: Mapped[str] = mapped_column(String, nullable=True)
scope: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
scope: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
issue_date: Mapped[datetime.datetime] = mapped_column(
TZDateTime(timezone=True), nullable=True
)
@ -251,7 +250,7 @@ class Token(canaille.oidc.models.Token, Base, SqlAlchemyModel):
revokation_date: Mapped[datetime.datetime] = mapped_column(
TZDateTime(timezone=True), nullable=True
)
audience: Mapped[List["Client"]] = relationship(
audience: Mapped[list["Client"]] = relationship(
"Client",
secondary=token_audience_association_table,
primaryjoin=id == token_audience_association_table.c.token_id,
@ -277,7 +276,7 @@ class Consent(canaille.oidc.models.Consent, Base, SqlAlchemyModel):
subject: Mapped["User"] = relationship()
client_id: Mapped[str] = mapped_column(ForeignKey("client.id"))
client: Mapped["Client"] = relationship()
scope: Mapped[List[str]] = mapped_column(MutableJson, nullable=True)
scope: Mapped[list[str]] = mapped_column(MutableJson, nullable=True)
issue_date: Mapped[datetime.datetime] = mapped_column(
TZDateTime(timezone=True), nullable=True
)

View file

@ -1,8 +1,4 @@
from enum import Enum
from typing import Dict
from typing import List
from typing import Optional
from typing import Union
from pydantic import BaseModel
from pydantic import ValidationInfo
@ -18,25 +14,25 @@ class SMTPSettings(BaseModel):
authentication.
"""
HOST: Optional[str] = "localhost"
HOST: str | None = "localhost"
"""The SMTP host."""
PORT: Optional[int] = 25
PORT: int | None = 25
"""The SMTP port."""
TLS: Optional[bool] = False
TLS: bool | None = False
"""Whether to use TLS to connect to the SMTP server."""
SSL: Optional[bool] = False
SSL: bool | None = False
"""Whether to use SSL to connect to the SMTP server."""
LOGIN: Optional[str] = None
LOGIN: str | None = None
"""The SMTP login."""
PASSWORD: Optional[str] = None
PASSWORD: str | None = None
"""The SMTP password."""
FROM_ADDR: Optional[str] = None
FROM_ADDR: str | None = None
"""The sender for Canaille mails.
Some mail provider might require a valid sender address.
@ -83,7 +79,7 @@ class ACLSettings(BaseModel):
to :attr:`READ` and :attr:`WRITE`. Users matching several filters will cumulate permissions.
"""
PERMISSIONS: List[Permission] = [Permission.EDIT_SELF, Permission.USE_OIDC]
PERMISSIONS: list[Permission] = [Permission.EDIT_SELF, Permission.USE_OIDC]
"""A list of :class:`Permission` users in the access control will be able
to manage. For example::
@ -95,7 +91,7 @@ class ACLSettings(BaseModel):
"impersonate_users",
]"""
READ: List[str] = [
READ: list[str] = [
"user_name",
"groups",
"lock_date",
@ -103,7 +99,7 @@ class ACLSettings(BaseModel):
"""A list of :class:`~canaille.core.models.User` attributes that users in
the ACL will be able to read."""
WRITE: List[str] = [
WRITE: list[str] = [
"photo",
"given_name",
"family_name",
@ -129,9 +125,9 @@ class ACLSettings(BaseModel):
@field_validator("READ")
def validate_read_values(
cls,
read: List[str],
read: list[str],
info: ValidationInfo,
) -> List[str]:
) -> list[str]:
from canaille.core.models import User
assert all(field in User.attributes for field in read)
@ -140,15 +136,15 @@ class ACLSettings(BaseModel):
@field_validator("WRITE")
def validate_write_values(
cls,
write: List[str],
write: list[str],
info: ValidationInfo,
) -> List[str]:
) -> list[str]:
from canaille.core.models import User
assert all(field in User.attributes for field in write)
return write
FILTER: Optional[Union[Dict[str, str], List[Dict[str, str]]]] = None
FILTER: dict[str, str] | list[dict[str, str]] | None = None
""":attr:`FILTER` can be:
- :py:data:`None`, in which case all the users will match this access control
@ -181,11 +177,11 @@ class CoreSettings(BaseModel):
Used for display purpose.
"""
LOGO: Optional[str] = None
LOGO: str | None = None
"""The logo of your organization, this is useful to make your organization
recognizable on login screens."""
FAVICON: Optional[str] = None
FAVICON: str | None = None
"""You favicon.
If unset and :attr:`LOGO` is set, then the logo will be used.
@ -197,20 +193,20 @@ class CoreSettings(BaseModel):
Defaults to ``default``. Theming is done with `flask-themer <https://github.com/tktech/flask-themer>`_.
"""
LANGUAGE: Optional[str] = None
LANGUAGE: str | None = None
"""If a language code is set, it will be used for every user.
If unset, the language is guessed according to the users browser.
"""
TIMEZONE: Optional[str] = None
TIMEZONE: str | None = None
"""The timezone in which datetimes will be displayed to the users (e.g.
``CEST``).
If unset, the server timezone will be used.
"""
SENTRY_DSN: Optional[str] = None
SENTRY_DSN: str | None = None
"""A `Sentry <https://sentry.io>`_ DSN to collect the exceptions.
This is useful for tracking errors in test and production environments.
@ -259,7 +255,7 @@ class CoreSettings(BaseModel):
Defaults to 2 days.
"""
LOGGING: Optional[Union[str, Dict]] = None
LOGGING: str | dict | None = None
"""Configures the logging output using the python logging configuration format:
- if :py:data:`None`, everything is logged in the standard error output
@ -282,14 +278,14 @@ class CoreSettings(BaseModel):
formatter = "default"
"""
SMTP: Optional[SMTPSettings] = None
SMTP: SMTPSettings | None = None
"""The settings related to SMTP and mail configuration.
If unset, mail-related features like password recovery won't be
enabled.
"""
ACL: Optional[Dict[str, ACLSettings]] = {"DEFAULT": ACLSettings()}
ACL: dict[str, ACLSettings] | None = {"DEFAULT": ACLSettings()}
"""Mapping of permission groups. See :class:`ACLSettings` for more details.
The ACL name can be freely chosen. For example::

View file

@ -4,7 +4,6 @@ import io
from dataclasses import astuple
from dataclasses import dataclass
from importlib import metadata
from typing import List
import wtforms
from flask import Blueprint
@ -189,7 +188,7 @@ class RegistrationPayload(VerificationPayload):
user_name: str
user_name_editable: bool
email: str
groups: List[str]
groups: list[str]
@bp.route("/invite", methods=["GET", "POST"])

View file

@ -1,8 +1,6 @@
import datetime
from typing import Annotated
from typing import ClassVar
from typing import List
from typing import Optional
from flask import current_app
@ -37,7 +35,7 @@ class User(Model):
and is case insensitive.
"""
password: Optional[str] = None
password: str | None = None
"""
This attribute is intended to be used as a means to set, replace,
or compare (i.e., filter for equality) a password. The cleartext
@ -82,7 +80,7 @@ class User(Model):
"never").
"""
preferred_language: Optional[str] = None
preferred_language: str | None = None
"""Indicates the user's preferred written or spoken languages and is
generally used for selecting a localized user interface.
@ -98,20 +96,20 @@ class User(Model):
negotiation cannot take place.
"""
family_name: Optional[str] = None
family_name: str | None = None
"""The family name of the User, or last name in most Western languages
(e.g., "Jensen" given the full name "Ms. Barbara Jane Jensen, III")."""
given_name: Optional[str] = None
given_name: str | None = None
"""The given name of the User, or first name in most Western languages
(e.g., "Barbara" given the full name "Ms. Barbara Jane Jensen, III")."""
formatted_name: Optional[str] = None
formatted_name: str | None = None
"""The full name, including all middle names, titles, and suffixes as
appropriate, formatted for display (e.g., "Ms. Barbara Jane Jensen,
III")."""
display_name: Optional[str] = None
display_name: str | None = None
"""The name of the user, suitable for display to end-users.
Each user returned MAY include a non-empty displayName value. The
@ -123,7 +121,7 @@ class User(Model):
when presenting it to end-users.
"""
emails: List[str] = []
emails: list[str] = []
"""Email addresses for the User.
The value SHOULD be specified according to [RFC5321]. Service
@ -137,7 +135,7 @@ class User(Model):
at the discretion of SCIM clients.
"""
phone_numbers: List[str] = []
phone_numbers: list[str] = []
"""Phone numbers for the user.
The value SHOULD be specified according to the format defined in
@ -150,14 +148,14 @@ class User(Model):
defined by the SCIM clients.
"""
formatted_address: Optional[str] = None
formatted_address: str | None = None
"""The full mailing address, formatted for display or use with a mailing
label.
This attribute MAY contain newlines.
"""
street: Optional[str] = None
street: str | None = None
"""The full street address component, which may include house number,
street name, P.O.
@ -165,16 +163,16 @@ class User(Model):
attribute MAY contain newlines.
"""
postal_code: Optional[str] = None
postal_code: str | None = None
"""The zip code or postal code component."""
locality: Optional[str] = None
locality: str | None = None
"""The city or locality component."""
region: Optional[str] = None
region: str | None = None
"""The state or region component."""
photo: Optional[str] = None
photo: str | None = None
"""A URI that is a uniform resource locator (as defined in Section 1.1.3 of
[RFC3986]) that points to a resource location representing the user's
image.
@ -192,7 +190,7 @@ class User(Model):
sizes: "photo" and "thumbnail".
"""
profile_url: Optional[str] = None
profile_url: str | None = None
"""A URI that is a uniform resource locator (as defined in Section 1.1.3 of
[RFC3986]) and that points to a location representing the user's online
profile (e.g., a web page).
@ -200,21 +198,21 @@ class User(Model):
URIs are canonicalized per Section 6.2 of [RFC3986].
"""
title: Optional[str] = None
title: str | None = None
"""The user's title, such as "Vice President"."""
organization: Optional[str] = None
organization: str | None = None
"""Identifies the name of an organization."""
employee_number: Optional[str] = None
employee_number: str | None = None
"""A string identifier, typically numeric or alphanumeric, assigned to a
person, typically based on order of hire or association with an
organization."""
department: Optional[str] = None
department: str | None = None
"""Identifies the name of a department."""
groups: List[Annotated["Group", {"backref": "members"}]] = []
groups: list[Annotated["Group", {"backref": "members"}]] = []
"""A list of groups to which the user belongs, either through direct
membership, through nested groups, or dynamically calculated.
@ -240,7 +238,7 @@ class User(Model):
"readOnly".
"""
lock_date: Optional[datetime.datetime] = None
lock_date: datetime.datetime | None = None
"""A DateTime indicating when the resource was locked."""
_readable_fields = None
@ -334,7 +332,7 @@ class Group(Model):
REQUIRED.
"""
members: List[Annotated["User", {"backref": "groups"}]] = []
members: list[Annotated["User", {"backref": "groups"}]] = []
"""A list of members of the Group.
While values MAY be added or removed, sub-attributes of members are
@ -348,4 +346,4 @@ class Group(Model):
"Group" resource schema.
"""
description: Optional[str] = None
description: str | None = None

View file

@ -1,7 +1,5 @@
import datetime
from typing import ClassVar
from typing import List
from typing import Optional
from canaille.backends.models import Model
from canaille.core.models import User
@ -18,11 +16,11 @@ class Client(Model):
identifier_attribute: ClassVar[str] = "client_id"
description: Optional[str] = None
preconsent: Optional[bool] = False
audience: List["Client"] = []
description: str | None = None
preconsent: bool | None = False
audience: list["Client"] = []
client_id: Optional[str]
client_id: str | None
"""REQUIRED.
OAuth 2.0 client identifier string. It SHOULD NOT be currently
@ -31,7 +29,7 @@ class Client(Model):
a registered client at its discretion.
"""
client_secret: Optional[str] = None
client_secret: str | None = None
"""OPTIONAL.
OAuth 2.0 client secret string. If issued, this MUST be unique for
@ -41,7 +39,7 @@ class Client(Model):
described in OAuth 2.0 [RFC6749], Section 2.3.1.
"""
client_id_issued_at: Optional[datetime.datetime] = None
client_id_issued_at: datetime.datetime | None = None
"""OPTIONAL.
Time at which the client identifier was issued. The time is
@ -49,7 +47,7 @@ class Client(Model):
measured in UTC until the date/time of issuance.
"""
client_secret_expires_at: Optional[datetime.datetime] = None
client_secret_expires_at: datetime.datetime | None = None
"""REQUIRED if "client_secret" is issued.
Time at which the client secret will expire or 0 if it will not
@ -58,7 +56,7 @@ class Client(Model):
expiration.
"""
redirect_uris: List[str] = []
redirect_uris: list[str] = []
"""Array of redirection URI strings for use in redirect-based flows such as
the authorization code and implicit flows.
@ -68,7 +66,7 @@ class Client(Model):
redirect-based flows MUST implement support for this metadata value.
"""
token_endpoint_auth_method: Optional[str] = None
token_endpoint_auth_method: str | None = None
"""String indicator of the requested authentication method for the token
endpoint. Values defined by this specification are:
@ -89,7 +87,7 @@ class Client(Model):
authentication scheme as specified in Section 2.3.1 of OAuth 2.0.
"""
grant_types: List[str] = ["authorization_code", "refresh_token"]
grant_types: list[str] = ["authorization_code", "refresh_token"]
"""Array of OAuth 2.0 grant type strings that the client can use at the
token endpoint. These grant types are defined as follows:
@ -125,7 +123,7 @@ class Client(Model):
client will use only the "authorization_code" Grant Type.
"""
response_types: List[str] = []
response_types: list[str] = []
"""
Array of the OAuth 2.0 response type strings that the client can
use at the authorization endpoint. These response types are
@ -146,7 +144,7 @@ class Client(Model):
default is that the client will use only the "code" response type.
"""
client_name: Optional[str] = None
client_name: str | None = None
"""Human-readable string name of the client to be presented to the end-user
during authorization.
@ -156,7 +154,7 @@ class Client(Model):
internationalized, as described in Section 2.2.
"""
client_uri: Optional[str] = None
client_uri: str | None = None
"""URL string of a web page providing information about the client.
If present, the server SHOULD display this URL to the end-user in a
@ -166,7 +164,7 @@ class Client(Model):
Section 2.2.
"""
logo_uri: Optional[str] = None
logo_uri: str | None = None
"""URL string that references a logo for the client.
If present, the server SHOULD display this image to the end-user
@ -175,7 +173,7 @@ class Client(Model):
described in Section 2.2.
"""
scope: List[str] = []
scope: list[str] = []
"""String containing a space-separated list of scope values (as described
in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when
requesting access tokens.
@ -185,7 +183,7 @@ class Client(Model):
default set of scopes.
"""
contacts: List[str] = None
contacts: list[str] = None
"""Array of strings representing ways to contact people responsible for
this client, typically email addresses.
@ -194,7 +192,7 @@ class Client(Model):
information on Privacy Considerations.
"""
tos_uri: Optional[str] = None
tos_uri: str | None = None
"""URL string that points to a human-readable terms of service document for
the client that describes a contractual relationship between the end-user
and the client that the end-user accepts when authorizing the client.
@ -205,7 +203,7 @@ class Client(Model):
described in Section 2.2.
"""
policy_uri: Optional[str] = None
policy_uri: str | None = None
"""URL string that points to a human-readable privacy policy document that
describes how the deployment organization collects, uses, retains, and
discloses personal data.
@ -216,7 +214,7 @@ class Client(Model):
described in Section 2.2.
"""
jwks_uri: Optional[str] = None
jwks_uri: str | None = None
"""URL string referencing the client's JSON Web Key (JWK) Set [RFC7517]
document, which contains the client's public keys.
@ -230,7 +228,7 @@ class Client(Model):
parameters MUST NOT both be present in the same request or response.
"""
jwk: Optional[str] = None
jwk: str | None = None
"""Client's JSON Web Key Set [RFC7517] document value, which contains the
client's public keys.
@ -242,7 +240,7 @@ class Client(Model):
parameters MUST NOT both be present in the same request or response.
"""
software_id: Optional[str] = None
software_id: str | None = None
"""A unique identifier string (e.g., a Universally Unique Identifier
(UUID)) assigned by the client developer or software publisher used by
registration endpoints to identify the client software to be dynamically
@ -257,7 +255,7 @@ class Client(Model):
authorization server.
"""
software_version: Optional[str] = None
software_version: str | None = None
"""A version identifier string for the client software identified by
"software_id".
@ -273,7 +271,7 @@ class Client(Model):
itself and is outside the scope of this specification.
"""
post_logout_redirect_uris: List[str] = []
post_logout_redirect_uris: list[str] = []
"""OPTIONAL.
Array of URLs supplied by the RP to which it MAY request that the
@ -296,14 +294,14 @@ class AuthorizationCode(Model):
code: str
client: "Client"
subject: User
redirect_uri: Optional[str]
response_type: Optional[str]
scope: List[str]
nonce: Optional[str]
redirect_uri: str | None
response_type: str | None
scope: list[str]
nonce: str | None
issue_date: datetime.datetime
lifetime: int
challenge: Optional[str]
challenge_method: Optional[str]
challenge: str | None
challenge_method: str | None
revokation_date: datetime.datetime
@ -318,11 +316,11 @@ class Token(Model):
subject: User
type: str
refresh_token: str
scope: List[str]
scope: list[str]
issue_date: datetime.datetime
lifetime: int
revokation_date: datetime.datetime
audience: List["Client"]
audience: list["Client"]
class Consent(Model):
@ -333,7 +331,7 @@ class Consent(Model):
consent_id: str
subject: User
client: "Client"
scope: List[str]
scope: list[str]
issue_date: datetime.datetime
revokation_date: datetime.datetime

View file

@ -1,6 +1,3 @@
from typing import List
from typing import Optional
from pydantic import BaseModel
@ -11,37 +8,33 @@ class JWTMappingSettings(BaseModel):
A ``user`` var is available.
"""
SUB: Optional[str] = "{{ user.user_name }}"
NAME: Optional[str] = (
SUB: str | None = "{{ user.user_name }}"
NAME: str | None = (
"{% if user.formatted_name %}{{ user.formatted_name }}{% endif %}"
)
PHONE_NUMBER: Optional[str] = (
PHONE_NUMBER: str | None = (
"{% if user.phone_numbers %}{{ user.phone_numbers[0] }}{% endif %}"
)
EMAIL: Optional[str] = (
EMAIL: str | None = (
"{% if user.preferred_email %}{{ user.preferred_email }}{% endif %}"
)
GIVEN_NAME: Optional[str] = (
"{% if user.given_name %}{{ user.given_name }}{% endif %}"
)
FAMILY_NAME: Optional[str] = (
GIVEN_NAME: str | None = "{% if user.given_name %}{{ user.given_name }}{% endif %}"
FAMILY_NAME: str | None = (
"{% if user.family_name %}{{ user.family_name }}{% endif %}"
)
PREFERRED_USERNAME: Optional[str] = (
PREFERRED_USERNAME: str | None = (
"{% if user.display_name %}{{ user.display_name }}{% endif %}"
)
LOCALE: Optional[str] = (
LOCALE: str | None = (
"{% if user.preferred_language %}{{ user.preferred_language }}{% endif %}"
)
ADDRESS: Optional[str] = (
ADDRESS: str | None = (
"{% if user.formatted_address %}{{ user.formatted_address }}{% endif %}"
)
PICTURE: Optional[str] = (
PICTURE: str | None = (
"{% if user.photo %}{{ url_for('core.account.photo', user=user, field='photo', _external=True) }}{% endif %}"
)
WEBSITE: Optional[str] = (
"{% if user.profile_url %}{{ user.profile_url }}{% endif %}"
)
WEBSITE: str | None = "{% if user.profile_url %}{{ user.profile_url }}{% endif %}"
class JWTSettings(BaseModel):
@ -53,19 +46,19 @@ class JWTSettings(BaseModel):
openssl rsa -in private.pem -pubout -outform PEM -out public.pem
"""
PRIVATE_KEY: Optional[str] = None
PRIVATE_KEY: str | None = None
"""The private key.
If :py:data:`None` and debug mode is enabled, then an in-memory key will be used.
"""
PUBLIC_KEY: Optional[str] = None
PUBLIC_KEY: str | None = None
"""The public key.
If :py:data:`None` and debug mode is enabled, then an in-memory key will be used.
"""
ISS: Optional[str] = None
ISS: str | None = None
"""The URI of the identity provider."""
KTY: str = "RSA"
@ -77,7 +70,7 @@ class JWTSettings(BaseModel):
EXP: int = 3600
"""The time the JWT will be valid, in seconds."""
MAPPING: Optional[JWTMappingSettings] = JWTMappingSettings()
MAPPING: JWTMappingSettings | None = JWTMappingSettings()
class OIDCSettings(BaseModel):
@ -94,7 +87,7 @@ class OIDCSettings(BaseModel):
:attr:`DYNAMIC_CLIENT_REGISTRATION_TOKENS`.
"""
DYNAMIC_CLIENT_REGISTRATION_TOKENS: Optional[List[str]] = None
DYNAMIC_CLIENT_REGISTRATION_TOKENS: list[str] | None = None
"""A list of tokens that can be used for dynamic client registration."""
REQUIRE_NONCE: bool = True

View file

@ -1,6 +1,5 @@
import datetime
from typing import ClassVar
from typing import List
from authlib.oauth2.rfc6749 import AuthorizationCodeMixin
from authlib.oauth2.rfc6749 import ClientMixin
@ -17,14 +16,14 @@ from .basemodels import Token as BaseToken
class Client(BaseClient, ClientMixin):
client_info_attributes: ClassVar[List[str]] = [
client_info_attributes: ClassVar[list[str]] = [
"client_id",
"client_secret",
"client_id_issued_at",
"client_secret_expires_at",
]
client_metadata_attributes: ClassVar[List[str]] = [
client_metadata_attributes: ClassVar[list[str]] = [
"client_name",
"contacts",
"client_uri",