alpha-python 0.6.3__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {alpha_python-0.6.3 → alpha_python-0.7.0}/PKG-INFO +1 -1
- {alpha_python-0.6.3 → alpha_python-0.7.0}/pyproject.toml +1 -1
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/__init__.py +16 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/__init__.py +2 -0
- alpha_python-0.7.0/src/alpha/interfaces/http_client.py +26 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/providers.py +6 -2
- alpha_python-0.7.0/src/alpha/interfaces/refresh_repository.py +60 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/providers/models/token.py +47 -9
- alpha_python-0.7.0/src/alpha/repositories/__init__.py +25 -0
- alpha_python-0.7.0/src/alpha/repositories/refresh/__init__.py +19 -0
- alpha_python-0.7.0/src/alpha/repositories/refresh/cache_repository.py +54 -0
- alpha_python-0.7.0/src/alpha/repositories/refresh/database_repository.py +145 -0
- alpha_python-0.7.0/src/alpha/repositories/refresh/file_repository.py +167 -0
- alpha_python-0.7.0/src/alpha/repositories/refresh/memory_repository.py +122 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/repositories/rest_api_repository.py +33 -19
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/services/authentication_service.py +98 -405
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/openapi_test/container.py +10 -2
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/openapi_test/models.py +48 -10
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha_python.egg-info/PKG-INFO +1 -1
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha_python.egg-info/SOURCES.txt +7 -0
- alpha_python-0.6.3/src/alpha/repositories/__init__.py +0 -9
- {alpha_python-0.6.3 → alpha_python-0.7.0}/LICENSE +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/README.md +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/setup.cfg +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/adapters/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/adapters/rest_api_unit_of_work.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/adapters/sqla_unit_of_work.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/cli.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/containers/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/containers/container.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/domain/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/domain/models/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/domain/models/base_model.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/domain/models/group.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/domain/models/life_cycle_base.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/domain/models/role.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/domain/models/user.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/encoder.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/exceptions.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/_type_conversion_matrix.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/_type_mapping.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/class_factories.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/default_field_factory.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/field_iterator.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/jwt_factory.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/logging_handler_factory.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/model_class_factory.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/models/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/models/factory_classes.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/password_factory.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/request_factory.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/response_factory.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/factories/type_factories.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/api_generate_handler.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/api_run_handler.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/base_handler.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/gen-code.sh +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/models/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/models/argument.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/models/command.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/models/section.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/models/subparser.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/run-api.sh +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/Dockerfile.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/README.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/__init__model.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/__init__test.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/__main__.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/base_model.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/controller.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/controller_test.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/dockerignore.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/encoder.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/git_push.sh.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/gitignore.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/model.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/openapi.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/param_type.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/requirements.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/security_controller_.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/setup.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/test-requirements.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/tox.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/travis.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/typing_utils.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/handlers/templates/python-flask/util.mustache +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/connectors/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/connectors/ldap_connector.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/connectors/oidc_connector.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/connectors/sql_alchemy.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/databases/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/databases/sql_alchemy.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/models/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/models/filter_operators.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/models/json_patch.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/models/order_by.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/models/query_clause.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/infra/models/search_filter.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/api_repository.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/attrs_instance.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/dataclass_instance.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/factories.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/handler.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/openapi_model.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/patchable.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/pydantic_instance.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/sql_database.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/sql_mapper.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/sql_repository.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/token_factory.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/unit_of_work.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/interfaces/updatable.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/mixins/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/mixins/group_lifecycle.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/mixins/jwt_provider.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/mixins/user_lifecycle.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/providers/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/providers/api_key_provider.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/providers/database_provider.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/providers/ldap_provider.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/providers/models/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/providers/models/credentials.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/providers/models/identity.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/providers/oidc_provider.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/py.typed +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/repositories/models/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/repositories/models/repository_model.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/repositories/sql_alchemy_repository.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/services/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/services/models/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/services/models/cookie.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/services/user_lifecycle_management.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/_http_codes.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/cookie.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/is_attrs.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/is_pydantic.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/logging_configurator.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/logging_level_checker.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/openapi_test/__init__.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/openapi_test/exceptions.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/openapi_test/orm.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/openapi_test/response.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/openapi_test/service.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/request_headers.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/response_object.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/secret_generator.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/verify_identity.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha/utils/version_checker.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha_python.egg-info/dependency_links.txt +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha_python.egg-info/entry_points.txt +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha_python.egg-info/requires.txt +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/src/alpha_python.egg-info/top_level.txt +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/tests/test_cli.py +0 -0
- {alpha_python-0.6.3 → alpha_python-0.7.0}/tests/test_encoder.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alpha-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Alpha is intended to be the first dependency you need to add to your Python application. It is a Python library which contains standard building blocks that can be used in applications that are used as APIs and/or make use of database interaction.
|
|
5
5
|
Author-email: Bart Reijling <bart@reijling.eu>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "alpha-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.0"
|
|
4
4
|
description = "Alpha is intended to be the first dependency you need to add to your Python application. It is a Python library which contains standard building blocks that can be used in applications that are used as APIs and/or make use of database interaction."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -64,6 +64,18 @@ from alpha.providers.oidc_provider import (
|
|
|
64
64
|
from alpha.repositories.models.repository_model import RepositoryModel
|
|
65
65
|
from alpha.repositories.rest_api_repository import RestApiRepository
|
|
66
66
|
from alpha.repositories.sql_alchemy_repository import SqlAlchemyRepository
|
|
67
|
+
from alpha.repositories.refresh.cache_repository import (
|
|
68
|
+
CacheRefreshRepository,
|
|
69
|
+
)
|
|
70
|
+
from alpha.repositories.refresh.database_repository import (
|
|
71
|
+
DatabaseRefreshRepository,
|
|
72
|
+
)
|
|
73
|
+
from alpha.repositories.refresh.file_repository import (
|
|
74
|
+
FileRefreshRepository,
|
|
75
|
+
)
|
|
76
|
+
from alpha.repositories.refresh.memory_repository import (
|
|
77
|
+
MemoryRefreshRepository,
|
|
78
|
+
)
|
|
67
79
|
from alpha.services.authentication_service import AuthenticationService
|
|
68
80
|
from alpha.services.user_lifecycle_management import UserLifecycleManagement
|
|
69
81
|
from alpha.utils.is_attrs import is_attrs
|
|
@@ -156,6 +168,10 @@ __all__ = [
|
|
|
156
168
|
"RepositoryModel",
|
|
157
169
|
"RestApiRepository",
|
|
158
170
|
"SqlAlchemyRepository",
|
|
171
|
+
"CacheRefreshRepository",
|
|
172
|
+
"DatabaseRefreshRepository",
|
|
173
|
+
"FileRefreshRepository",
|
|
174
|
+
"MemoryRefreshRepository",
|
|
159
175
|
"AuthenticationService",
|
|
160
176
|
"UserLifecycleManagement",
|
|
161
177
|
"is_attrs",
|
|
@@ -9,6 +9,7 @@ from alpha.interfaces.patchable import Patchable
|
|
|
9
9
|
# import all repository related interfaces
|
|
10
10
|
from alpha.interfaces.api_repository import ApiRepository
|
|
11
11
|
from alpha.interfaces.sql_repository import SqlRepository
|
|
12
|
+
from alpha.interfaces.refresh_repository import RefreshRepository
|
|
12
13
|
|
|
13
14
|
# import all database related interfaces
|
|
14
15
|
from alpha.interfaces.sql_mapper import SqlMapper
|
|
@@ -35,6 +36,7 @@ __all__ = [
|
|
|
35
36
|
"Patchable",
|
|
36
37
|
"ApiRepository",
|
|
37
38
|
"SqlRepository",
|
|
39
|
+
"RefreshRepository",
|
|
38
40
|
"SqlMapper",
|
|
39
41
|
"SqlDatabase",
|
|
40
42
|
"UnitOfWork",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Protocol, Any, runtime_checkable
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@runtime_checkable
|
|
5
|
+
class HTTPClient(Protocol):
|
|
6
|
+
"""Interface for HTTP clients like requests, httpx or a custom
|
|
7
|
+
implementation.
|
|
8
|
+
|
|
9
|
+
This interface is compatible with the popular synchronous HTTP client
|
|
10
|
+
libraries, for example, the `requests` library, `httpx` library or any
|
|
11
|
+
custom implementation that follows the same method signatures.
|
|
12
|
+
|
|
13
|
+
This interface defines the methods that an HTTP client should implement to
|
|
14
|
+
be compatible with the REST API repository. It includes methods for making
|
|
15
|
+
HTTP requests (POST, GET, DELETE, PUT, PATCH) and allows for additional
|
|
16
|
+
parameters to be passed as needed.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
cookies: Any
|
|
20
|
+
headers: Any
|
|
21
|
+
|
|
22
|
+
def post(self, url: str, json: Any = None, **kwargs: Any) -> Any: ...
|
|
23
|
+
def get(self, url: str, **kwargs: Any) -> Any: ...
|
|
24
|
+
def delete(self, url: str, **kwargs: Any) -> Any: ...
|
|
25
|
+
def put(self, url: str, json: Any = None, **kwargs: Any) -> Any: ...
|
|
26
|
+
def patch(self, url: str, json: Any = None, **kwargs: Any) -> Any: ...
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"""This module contains interfaces for various types of identity providers."""
|
|
2
2
|
|
|
3
|
-
from typing import ClassVar, Protocol, runtime_checkable
|
|
3
|
+
from typing import TYPE_CHECKING, ClassVar, Protocol, runtime_checkable, Any
|
|
4
4
|
|
|
5
5
|
from alpha.interfaces.token_factory import TokenFactory
|
|
6
6
|
from alpha.providers.models.credentials import PasswordCredentials
|
|
7
7
|
from alpha.providers.models.identity import Identity
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from alpha.providers.models.token import Token
|
|
11
|
+
else:
|
|
12
|
+
Token = Any
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
@runtime_checkable
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Protocol, Any
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from alpha.providers.models.token import Token
|
|
5
|
+
else:
|
|
6
|
+
Token = Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RefreshRepository(Protocol):
|
|
10
|
+
"""Repository interface for managing refresh tokens."""
|
|
11
|
+
|
|
12
|
+
def get(self, token: str) -> Token:
|
|
13
|
+
"""Get a token by its value.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
token
|
|
18
|
+
Token value.
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
Token
|
|
23
|
+
Token object with the given token value.
|
|
24
|
+
"""
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
def create(self, subject: str) -> Token:
|
|
28
|
+
"""Create a new token for a given subject.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
subject
|
|
33
|
+
Subject identifier
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
Token
|
|
38
|
+
Newly created token object.
|
|
39
|
+
"""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
def delete(self, token: str) -> None:
|
|
43
|
+
"""Delete a token by its value.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
token
|
|
48
|
+
Token value.
|
|
49
|
+
"""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
def delete_all(self, subject: str) -> None:
|
|
53
|
+
"""Delete all tokens for a given subject.
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
subject
|
|
58
|
+
Subject identifier.
|
|
59
|
+
"""
|
|
60
|
+
...
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from uuid import UUID
|
|
2
|
-
from datetime import datetime
|
|
2
|
+
from datetime import datetime, timedelta, timezone
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Any, Literal
|
|
5
5
|
|
|
6
6
|
from alpha.domain.models.base_model import BaseDomainModel
|
|
7
|
+
from alpha.utils.secret_generator import generate_secret
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
@dataclass
|
|
@@ -79,6 +80,40 @@ class Token(BaseDomainModel):
|
|
|
79
80
|
),
|
|
80
81
|
)
|
|
81
82
|
|
|
83
|
+
@classmethod
|
|
84
|
+
def create_refresh(
|
|
85
|
+
cls,
|
|
86
|
+
subject: str,
|
|
87
|
+
max_age_seconds: int = 7 * 24 * 3600,
|
|
88
|
+
token_length: int = 32,
|
|
89
|
+
) -> "Token":
|
|
90
|
+
"""Factory method to create a new Refresh Token instance.
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
subject
|
|
95
|
+
The subject or user associated with the token.
|
|
96
|
+
max_age_seconds, optional
|
|
97
|
+
Optional maximum age of the token in seconds. Defaults to 7 days.
|
|
98
|
+
If provided, the expires_at will be set to created_at +
|
|
99
|
+
max_age_seconds.
|
|
100
|
+
token_length, optional
|
|
101
|
+
Optional length of the token value. Defaults to 32.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
Token
|
|
106
|
+
A new Refresh Token instance with the provided attributes and generated id and created_at.
|
|
107
|
+
"""
|
|
108
|
+
return cls(
|
|
109
|
+
value=generate_secret(token_length),
|
|
110
|
+
token_type="Refresh",
|
|
111
|
+
subject=subject,
|
|
112
|
+
created_at=datetime.now(tz=timezone.utc),
|
|
113
|
+
expires_at=datetime.now(tz=timezone.utc)
|
|
114
|
+
+ timedelta(seconds=max_age_seconds),
|
|
115
|
+
)
|
|
116
|
+
|
|
82
117
|
def to_dict(self) -> dict[str, str | None]:
|
|
83
118
|
"""Converts the Token instance to a dictionary.
|
|
84
119
|
|
|
@@ -97,15 +132,18 @@ class Token(BaseDomainModel):
|
|
|
97
132
|
- "expires_at": ISO format datetime string for when the token
|
|
98
133
|
expires or None.
|
|
99
134
|
"""
|
|
100
|
-
|
|
101
|
-
|
|
135
|
+
|
|
136
|
+
obj: dict[str, str | None] = {
|
|
102
137
|
"value": self.value,
|
|
103
138
|
"subject": self.subject,
|
|
104
139
|
"token_type": self.token_type,
|
|
105
|
-
"created_at": (
|
|
106
|
-
self.created_at.isoformat() if self.created_at else None
|
|
107
|
-
),
|
|
108
|
-
"expires_at": (
|
|
109
|
-
self.expires_at.isoformat() if self.expires_at else None
|
|
110
|
-
),
|
|
111
140
|
}
|
|
141
|
+
|
|
142
|
+
if self.id:
|
|
143
|
+
obj["id"] = str(self.id)
|
|
144
|
+
if self.created_at:
|
|
145
|
+
obj["created_at"] = self.created_at.isoformat()
|
|
146
|
+
if self.expires_at:
|
|
147
|
+
obj["expires_at"] = self.expires_at.isoformat()
|
|
148
|
+
|
|
149
|
+
return obj
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from alpha.repositories.models.repository_model import RepositoryModel
|
|
2
|
+
from alpha.repositories.refresh.cache_repository import (
|
|
3
|
+
CacheRefreshRepository,
|
|
4
|
+
)
|
|
5
|
+
from alpha.repositories.refresh.database_repository import (
|
|
6
|
+
DatabaseRefreshRepository,
|
|
7
|
+
)
|
|
8
|
+
from alpha.repositories.refresh.file_repository import (
|
|
9
|
+
FileRefreshRepository,
|
|
10
|
+
)
|
|
11
|
+
from alpha.repositories.refresh.memory_repository import (
|
|
12
|
+
MemoryRefreshRepository,
|
|
13
|
+
)
|
|
14
|
+
from alpha.repositories.rest_api_repository import RestApiRepository
|
|
15
|
+
from alpha.repositories.sql_alchemy_repository import SqlAlchemyRepository
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"RepositoryModel",
|
|
19
|
+
"RestApiRepository",
|
|
20
|
+
"SqlAlchemyRepository",
|
|
21
|
+
"CacheRefreshRepository",
|
|
22
|
+
"DatabaseRefreshRepository",
|
|
23
|
+
"FileRefreshRepository",
|
|
24
|
+
"MemoryRefreshRepository",
|
|
25
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from alpha.repositories.refresh.cache_repository import (
|
|
2
|
+
CacheRefreshRepository,
|
|
3
|
+
)
|
|
4
|
+
from alpha.repositories.refresh.database_repository import (
|
|
5
|
+
DatabaseRefreshRepository,
|
|
6
|
+
)
|
|
7
|
+
from alpha.repositories.refresh.file_repository import (
|
|
8
|
+
FileRefreshRepository,
|
|
9
|
+
)
|
|
10
|
+
from alpha.repositories.refresh.memory_repository import (
|
|
11
|
+
MemoryRefreshRepository,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"CacheRefreshRepository",
|
|
16
|
+
"DatabaseRefreshRepository",
|
|
17
|
+
"FileRefreshRepository",
|
|
18
|
+
"MemoryRefreshRepository",
|
|
19
|
+
]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from alpha.providers.models.token import Token
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CacheRefreshRepository:
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
cache_connector: Any,
|
|
10
|
+
token_model: type[Token] = Token,
|
|
11
|
+
token_max_age_seconds: int = 7 * 24 * 3600,
|
|
12
|
+
token_length: int = 32,
|
|
13
|
+
):
|
|
14
|
+
"""Initialize the CacheRefreshRepository with the given cache connector.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
cache_connector
|
|
19
|
+
The cache connector instance to use for cache operations.
|
|
20
|
+
token_model, optional
|
|
21
|
+
The model class for tokens, by default Token. The model class
|
|
22
|
+
should have a `from_dict` class method that takes a dictionary and
|
|
23
|
+
returns an instance of the model. The dictionary will have the same
|
|
24
|
+
structure as the token data in the JSON file. The model class
|
|
25
|
+
should also have a `to_dict` method that converts an instance of
|
|
26
|
+
the model to a dictionary with the same structure as the token data
|
|
27
|
+
in the JSON file. The model class should also have a
|
|
28
|
+
`create_refresh` class method that creates a new refresh token.
|
|
29
|
+
token_max_age_seconds, optional
|
|
30
|
+
The maximum age of a token in seconds, by default the equivalent of
|
|
31
|
+
7 days in seconds
|
|
32
|
+
token_length, optional
|
|
33
|
+
The length of the generated token string, by default 32 characters
|
|
34
|
+
"""
|
|
35
|
+
self._cache_connector = cache_connector
|
|
36
|
+
self._token_model = token_model
|
|
37
|
+
self._token_max_age_seconds = token_max_age_seconds
|
|
38
|
+
self._token_length = token_length
|
|
39
|
+
|
|
40
|
+
def get(self, token: str) -> Token:
|
|
41
|
+
"""Get a token by its value."""
|
|
42
|
+
raise NotImplementedError("Method not implemented yet.")
|
|
43
|
+
|
|
44
|
+
def create(self, subject: str) -> Token:
|
|
45
|
+
"""Create a new token for a given subject."""
|
|
46
|
+
raise NotImplementedError("Method not implemented yet.")
|
|
47
|
+
|
|
48
|
+
def delete(self, token: str) -> None:
|
|
49
|
+
"""Delete a token by its value."""
|
|
50
|
+
raise NotImplementedError("Method not implemented yet.")
|
|
51
|
+
|
|
52
|
+
def delete_all(self, subject: str) -> None:
|
|
53
|
+
"""Delete all tokens for a given subject."""
|
|
54
|
+
raise NotImplementedError("Method not implemented yet.")
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from alpha import exceptions
|
|
2
|
+
from alpha.infra.connectors.sql_alchemy import SqlAlchemyDatabase
|
|
3
|
+
from alpha.providers.models.token import Token
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DatabaseRefreshRepository:
|
|
7
|
+
"""Implementation of the RefreshRepository interface for database
|
|
8
|
+
operations.
|
|
9
|
+
|
|
10
|
+
This repository uses a SQLAlchemy database connector to manage refresh
|
|
11
|
+
tokens in a database. It provides methods to get, create, delete, and
|
|
12
|
+
delete all refresh tokens for a given subject. The tokens are stored in a
|
|
13
|
+
database table which is mapped to the token model.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
database_connector: SqlAlchemyDatabase,
|
|
19
|
+
token_model: type[Token] = Token,
|
|
20
|
+
token_max_age_seconds: int = 7 * 24 * 3600,
|
|
21
|
+
token_length: int = 32,
|
|
22
|
+
):
|
|
23
|
+
"""Initialize the DatabaseRefreshRepository with the given database
|
|
24
|
+
connector and token model.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
database_connector
|
|
29
|
+
The database connector instance to use for database operations.
|
|
30
|
+
token_model, optional
|
|
31
|
+
The model class for tokens, by default Token. The model class
|
|
32
|
+
should have a `from_dict` class method that takes a dictionary and
|
|
33
|
+
returns an instance of the model. The dictionary will have the same
|
|
34
|
+
structure as the token data in the JSON file. The model class
|
|
35
|
+
should also have a `to_dict` method that converts an instance of
|
|
36
|
+
the model to a dictionary with the same structure as the token data
|
|
37
|
+
in the JSON file. The model class should also have a
|
|
38
|
+
`create_refresh` class method that creates a new refresh token.
|
|
39
|
+
token_max_age_seconds, optional
|
|
40
|
+
The maximum age of a token in seconds, by default the equivalent of
|
|
41
|
+
7 days in seconds
|
|
42
|
+
token_length, optional
|
|
43
|
+
The length of the generated token string, by default 32 characters
|
|
44
|
+
"""
|
|
45
|
+
self._database_connector = database_connector
|
|
46
|
+
self._token_model = token_model
|
|
47
|
+
self._token_max_age_seconds = token_max_age_seconds
|
|
48
|
+
self._token_length = token_length
|
|
49
|
+
|
|
50
|
+
def get(self, token: str) -> Token:
|
|
51
|
+
"""Get a token by its value.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
token
|
|
56
|
+
The value of the token to retrieve.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
Token
|
|
61
|
+
The token object corresponding to the given value.
|
|
62
|
+
|
|
63
|
+
Raises
|
|
64
|
+
------
|
|
65
|
+
NotFoundException
|
|
66
|
+
If the token is not found in the database.
|
|
67
|
+
"""
|
|
68
|
+
with self._database_connector.get_session() as session:
|
|
69
|
+
result = (
|
|
70
|
+
session.query(self._token_model)
|
|
71
|
+
.filter_by(value=token)
|
|
72
|
+
.one_or_none()
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if result is None:
|
|
76
|
+
raise exceptions.NotFoundException("Refresh token not found")
|
|
77
|
+
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
def create(self, subject: str) -> Token:
|
|
81
|
+
"""Create a new token for a given subject.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
subject
|
|
86
|
+
The subject for which to create the token.
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
Token
|
|
91
|
+
The newly created token object.
|
|
92
|
+
"""
|
|
93
|
+
token = self._token_model.create_refresh(
|
|
94
|
+
subject=subject,
|
|
95
|
+
max_age_seconds=self._token_max_age_seconds,
|
|
96
|
+
token_length=self._token_length,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
with self._database_connector.get_session() as session:
|
|
100
|
+
session.add(token)
|
|
101
|
+
session.commit()
|
|
102
|
+
session.refresh(token)
|
|
103
|
+
return token
|
|
104
|
+
|
|
105
|
+
def delete(self, token: str) -> None:
|
|
106
|
+
"""Delete a token by its value.
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
token
|
|
111
|
+
The value of the token to delete.
|
|
112
|
+
|
|
113
|
+
Raises
|
|
114
|
+
------
|
|
115
|
+
NotFoundException
|
|
116
|
+
If the token is not found in the database.
|
|
117
|
+
"""
|
|
118
|
+
with self._database_connector.get_session() as session:
|
|
119
|
+
token_obj = (
|
|
120
|
+
session.query(self._token_model)
|
|
121
|
+
.filter_by(value=token)
|
|
122
|
+
.one_or_none()
|
|
123
|
+
)
|
|
124
|
+
if token_obj is None:
|
|
125
|
+
raise exceptions.NotFoundException("Refresh token not found")
|
|
126
|
+
session.delete(token_obj)
|
|
127
|
+
session.commit()
|
|
128
|
+
|
|
129
|
+
def delete_all(self, subject: str) -> None:
|
|
130
|
+
"""Delete all tokens for a given subject.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
subject
|
|
135
|
+
The subject for which to delete all tokens.
|
|
136
|
+
"""
|
|
137
|
+
with self._database_connector.get_session() as session:
|
|
138
|
+
tokens = (
|
|
139
|
+
session.query(self._token_model)
|
|
140
|
+
.filter_by(subject=subject)
|
|
141
|
+
.all()
|
|
142
|
+
)
|
|
143
|
+
for token in tokens:
|
|
144
|
+
session.delete(token)
|
|
145
|
+
session.commit()
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from alpha import exceptions
|
|
5
|
+
from alpha.providers.models.token import Token
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FileRefreshRepository:
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
file_path: str | None = None,
|
|
12
|
+
token_model: type[Token] = Token,
|
|
13
|
+
token_max_age_seconds: int = 7 * 24 * 3600,
|
|
14
|
+
token_length: int = 32,
|
|
15
|
+
):
|
|
16
|
+
"""Initialize the FileRefreshRepository with the given file path.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
file_path
|
|
21
|
+
File path for storing refresh tokens if using file storage,
|
|
22
|
+
by default None. When the value is None the file will be stored in
|
|
23
|
+
the current working directory. The file should be a JSON file that
|
|
24
|
+
stores an object of refresh tokens. If the file does not exist, it
|
|
25
|
+
will be created automatically. The structure of the JSON file
|
|
26
|
+
should be as follows:
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"<TOKEN_VALUE>": {
|
|
30
|
+
"value": "<TOKEN_VALUE>",
|
|
31
|
+
"token_type": "Refresh",
|
|
32
|
+
"subject": "<SUBJECT>",
|
|
33
|
+
"created_at": "<ISO8601_DATETIME>",
|
|
34
|
+
"expires_at": "<ISO8601_DATETIME>"
|
|
35
|
+
},
|
|
36
|
+
...
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
token_model, optional
|
|
40
|
+
The model class for tokens, by default Token. The model class
|
|
41
|
+
should have a `from_dict` class method that takes a dictionary and
|
|
42
|
+
returns an instance of the model. The dictionary will have the same
|
|
43
|
+
structure as the token data in the JSON file. The model class
|
|
44
|
+
should also have a `to_dict` method that converts an instance of
|
|
45
|
+
the model to a dictionary with the same structure as the token data
|
|
46
|
+
in the JSON file. The model class should also have a
|
|
47
|
+
`create_refresh` class method that creates a new refresh token.
|
|
48
|
+
token_max_age_seconds, optional
|
|
49
|
+
The maximum age of a token in seconds, by default the equivalent of
|
|
50
|
+
7 days in seconds
|
|
51
|
+
token_length, optional
|
|
52
|
+
The length of the generated token string, by default 32 characters
|
|
53
|
+
"""
|
|
54
|
+
self._file_path = file_path or "refresh_tokens.json"
|
|
55
|
+
self._token_model = token_model
|
|
56
|
+
self._token_max_age_seconds = token_max_age_seconds
|
|
57
|
+
self._token_length = token_length
|
|
58
|
+
|
|
59
|
+
with open(self._file_path, "a+") as file:
|
|
60
|
+
file.seek(0)
|
|
61
|
+
try:
|
|
62
|
+
json.load(file)
|
|
63
|
+
except json.JSONDecodeError:
|
|
64
|
+
file.seek(0)
|
|
65
|
+
file.write("{}")
|
|
66
|
+
file.truncate()
|
|
67
|
+
|
|
68
|
+
def get(self, token: str) -> Token:
|
|
69
|
+
"""Get a token by its value.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
token
|
|
74
|
+
The value of the token to retrieve.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
Token
|
|
79
|
+
The token object corresponding to the given value.
|
|
80
|
+
"""
|
|
81
|
+
with open(self._file_path, "r") as file:
|
|
82
|
+
tokens_data: dict[str, dict[str, Any]] = json.load(file)
|
|
83
|
+
token_data = tokens_data.get(token, None)
|
|
84
|
+
|
|
85
|
+
if not token_data:
|
|
86
|
+
raise exceptions.NotFoundException("Refresh token not found")
|
|
87
|
+
|
|
88
|
+
return self._token_model.from_dict(token_data)
|
|
89
|
+
|
|
90
|
+
def create(self, subject: str) -> Token:
|
|
91
|
+
"""Create a new token for a given subject.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
subject
|
|
96
|
+
The subject for which to create a new token.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
Token
|
|
101
|
+
The newly created token object.
|
|
102
|
+
"""
|
|
103
|
+
token = self._token_model.create_refresh(
|
|
104
|
+
subject=subject,
|
|
105
|
+
max_age_seconds=self._token_max_age_seconds,
|
|
106
|
+
token_length=self._token_length,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
with open(self._file_path, "r") as file:
|
|
110
|
+
tokens_data: dict[str, dict[str, Any]] = json.load(file)
|
|
111
|
+
|
|
112
|
+
tokens_data[token.value] = token.to_dict()
|
|
113
|
+
|
|
114
|
+
with open(self._file_path, "w") as file:
|
|
115
|
+
json.dump(tokens_data, file, indent=4)
|
|
116
|
+
|
|
117
|
+
return token
|
|
118
|
+
|
|
119
|
+
def delete(self, token: str) -> None:
|
|
120
|
+
"""Delete a token by its value.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
token
|
|
125
|
+
The value of the token to delete.
|
|
126
|
+
|
|
127
|
+
Raises
|
|
128
|
+
------
|
|
129
|
+
NotFoundException
|
|
130
|
+
If the token with the given value is not found in the file.
|
|
131
|
+
"""
|
|
132
|
+
with open(self._file_path, "r") as file:
|
|
133
|
+
tokens_data: dict[str, dict[str, Any]] = json.load(file)
|
|
134
|
+
|
|
135
|
+
if token in tokens_data:
|
|
136
|
+
del tokens_data[token]
|
|
137
|
+
|
|
138
|
+
with open(self._file_path, "w") as file:
|
|
139
|
+
json.dump(tokens_data, file, indent=4)
|
|
140
|
+
else:
|
|
141
|
+
raise exceptions.NotFoundException("Refresh token not found")
|
|
142
|
+
|
|
143
|
+
def delete_all(self, subject: str) -> None:
|
|
144
|
+
"""Delete all tokens for a given subject.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
subject
|
|
149
|
+
The subject for which to delete all tokens.
|
|
150
|
+
"""
|
|
151
|
+
with open(self._file_path, "r") as file:
|
|
152
|
+
tokens_data: dict[str, dict[str, Any]] = json.load(file)
|
|
153
|
+
|
|
154
|
+
tokens_to_delete = [
|
|
155
|
+
token
|
|
156
|
+
for token, data in tokens_data.items()
|
|
157
|
+
if data["subject"] == subject
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
if not tokens_to_delete:
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
for token in tokens_to_delete:
|
|
164
|
+
del tokens_data[token]
|
|
165
|
+
|
|
166
|
+
with open(self._file_path, "w") as file:
|
|
167
|
+
json.dump(tokens_data, file, indent=4)
|