ab-identity-context 0.1.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.
@@ -0,0 +1,212 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ uv.lock
102
+
103
+ # poetry
104
+ # For packages, we do not commit the lock file,
105
+ # since we are not controlling the environment,
106
+ # refer to https://python-poetry.org/docs/basic-usage/#as-a-library-developer
107
+ poetry.lock
108
+ poetry.toml
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
113
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
114
+ pdm.lock
115
+ pdm.toml
116
+ .pdm-python
117
+ .pdm-build/
118
+
119
+ # pixi
120
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
121
+ pixi.lock
122
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
123
+ # in the .venv directory. It is recommended not to include this directory in version control.
124
+ .pixi
125
+
126
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
127
+ __pypackages__/
128
+
129
+ # Celery stuff
130
+ celerybeat-schedule
131
+ celerybeat.pid
132
+
133
+ # SageMath parsed files
134
+ *.sage.py
135
+
136
+ # Environments
137
+ .env
138
+ .envrc
139
+ .venv
140
+ env/
141
+ venv/
142
+ ENV/
143
+ env.bak/
144
+ venv.bak/
145
+
146
+ # Spyder project settings
147
+ .spyderproject
148
+ .spyproject
149
+
150
+ # Rope project settings
151
+ .ropeproject
152
+
153
+ # mkdocs documentation
154
+ /site
155
+
156
+ # mypy
157
+ .mypy_cache/
158
+ .dmypy.json
159
+ dmypy.json
160
+
161
+ # Pyre type checker
162
+ .pyre/
163
+
164
+ # pytype static type analyzer
165
+ .pytype/
166
+
167
+ # Cython debug symbols
168
+ cython_debug/
169
+
170
+ # PyCharm
171
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
172
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
173
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
174
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
175
+ #.idea/
176
+
177
+ # Abstra
178
+ # Abstra is an AI-powered process automation framework.
179
+ # Ignore directories containing user credentials, local state, and settings.
180
+ # Learn more at https://abstra.io/docs
181
+ .abstra/
182
+
183
+ # Visual Studio Code
184
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
185
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
186
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
187
+ # you could uncomment the following to ignore the entire vscode folder
188
+ #.vscode/
189
+
190
+ # Ruff stuff:
191
+ .ruff_cache/
192
+
193
+ # PyPI configuration file
194
+ .pypirc
195
+
196
+ # Cursor
197
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
198
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
199
+ # refer to https://docs.cursor.com/context/ignore-files
200
+ .cursorignore
201
+ .cursorindexingignore
202
+
203
+ # Marimo
204
+ marimo/_static/
205
+ marimo/_lsp/
206
+ __marimo__/
207
+
208
+ # Streamlit
209
+ .streamlit/secrets.toml
210
+
211
+ # Generated Files
212
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Matthew Coulter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.4
2
+ Name: ab-identity-context
3
+ Version: 0.1.0
4
+ Summary: Glue token validation + user service to provide identity context in FastAPI
5
+ Author-email: Matt Coulter <mattcoul7@gmail.com>
6
+ License-File: LICENSE
7
+ Requires-Python: <4.0,>=3.12
8
+ Requires-Dist: ab-client-token-validator>=0.1.0
9
+ Requires-Dist: ab-client-user>=0.1.0
10
+ Requires-Dist: ab-dependency>=0.1.9
11
+ Requires-Dist: pydantic>=2.11.10
12
+ Provides-Extra: all
13
+ Requires-Dist: fastapi>=0.115.14; extra == 'all'
14
+ Provides-Extra: fastapi
15
+ Requires-Dist: fastapi>=0.115.14; extra == 'fastapi'
16
+ Description-Content-Type: text/markdown
17
+
18
+ <div align="center">
19
+
20
+ # Python Package Template
21
+
22
+ The template repository for creating python packages, shared across auth-broker.
23
+
24
+ ![Python](https://img.shields.io/badge/Python-3.12-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)
25
+ ![UV](https://img.shields.io/badge/UV-Fast-6E40C9?style=for-the-badge)
26
+ ![Hatchling](https://img.shields.io/badge/Hatchling-PEP517-6E40C9?style=for-the-badge)
27
+ ![Ruff](https://img.shields.io/badge/Ruff-Lint-000000?style=for-the-badge)
28
+ ![Pre-commit](https://img.shields.io/badge/Pre--commit-Hooks-000000?style=for-the-badge)
29
+ ![Pytest](https://img.shields.io/badge/Pytest-Unit%2BAsync-08979C?style=for-the-badge)
30
+ ![Coverage](https://img.shields.io/badge/Cov-Reports-08979C?style=for-the-badge)
31
+ ![GitHub Actions](https://img.shields.io/badge/Actions-CI%2FCD-F7B500?style=for-the-badge&logo=github-actions)
32
+ ![PyPI](https://img.shields.io/badge/PyPI-Publish-6E40C9?style=for-the-badge)
33
+ ![Makefile](https://img.shields.io/badge/Makefile-Scripts-F7B500?style=for-the-badge)
34
+
35
+ 🦜🕸️
36
+
37
+ [![CI](https://github.com/auth-broker/package-template/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/auth-broker/package-template/actions/workflows/ci.yaml)
38
+
39
+ </div>
40
+
41
+ ______________________________________________________________________
42
+
43
+ ## Table of Contents
44
+
45
+ <!-- toc -->
46
+
47
+ - [Introduction](#introduction)
48
+ - [Quick Start](#quick-start)
49
+ - [Installation](#installation)
50
+ - [Usage](#usage)
51
+ - [Formatting and linting](#formatting-and-linting)
52
+ - [CICD](#cicd)
53
+
54
+ <!-- tocstop -->
55
+
56
+ ______________________________________________________________________
57
+
58
+ ## Introduction
59
+
60
+ This template repository aims to create a reusable package template which
61
+ streamlines the creation and publishing of isolated python packages in auth-broker.
62
+ This is aligned with the engineering vision @ auth-broker for better modularisation and
63
+ reusability of code.
64
+
65
+ ______________________________________________________________________
66
+
67
+ ## Quick Start
68
+
69
+ Since this is just a package, and not a service, there is no real "run" action.
70
+ But you can run the tests immediately.
71
+
72
+ Here are a list of available commands via make.
73
+
74
+ ### Bare Metal (i.e. your machine)
75
+
76
+ 1. `make install` - install the required dependencies.
77
+ 1. `make test` - runs the tests.
78
+
79
+ ### Docker
80
+
81
+ 1. `make build-docker` - build the docker image.
82
+ 1. `make run-docker` - run the docker compose services.
83
+ 1. `make test-docker` - run the tests in docker.
84
+ 1. `make clean-docker` - remove all docker containers etc.
85
+
86
+ ______________________________________________________________________
87
+
88
+ ## Installation
89
+
90
+ ### For Dev work on the repo
91
+
92
+ Install `uv`, (_if you haven't already_)
93
+ https://docs.astral.sh/uv/getting-started/installation/#installation-methods
94
+
95
+ ```shell
96
+ brew install uv
97
+ ```
98
+
99
+ Initialise pre-commit (validates ruff on commit.)
100
+
101
+ ```shell
102
+ uv run pre-commit install
103
+ ```
104
+
105
+ Install dependencies (including dev dependencies)
106
+
107
+ ```shell
108
+ uv sync
109
+ ```
110
+
111
+ If you are adding a new dev dependency, please run:
112
+
113
+ ```shell
114
+ uv add --dev {your-new-package}
115
+ ```
116
+
117
+ ### Namespaces
118
+
119
+ Packages all share the same namespace `ab_core`. To import this package into
120
+ your project:
121
+
122
+ ```python
123
+ from ab_core.template import placeholder_func
124
+ ```
125
+
126
+ We encourage you to make your package available to all of ab via this
127
+ `ab_core` namespace. The goal is to streamline development, POCs and overall
128
+ collaboration.
129
+
130
+ ______________________________________________________________________
131
+
132
+ ## Usage
133
+
134
+ ### Adding the dependency to your project
135
+
136
+ The library is available on PyPI. You can install it using the following
137
+ command:
138
+
139
+ **Using pip**:
140
+
141
+ ```shell
142
+ pip install package-identity-context
143
+ ```
144
+
145
+ **Using UV**
146
+
147
+ Note: there is currently no nice way like poetry, hence we still needd to
148
+ provide the full url. https://github.com/astral-sh/uv/issues/10140
149
+
150
+ Add the dependency
151
+
152
+ ```shell
153
+ uv add package-identity-context
154
+ ```
155
+
156
+ **Using poetry**:
157
+
158
+ Then run the following command to install the package:
159
+
160
+ ```shell
161
+ poetry add package-identity-context
162
+ ```
163
+
164
+ ### How tos
165
+
166
+ **Example Usage**
167
+
168
+ ```python
169
+ # Please update this based on your package!
170
+
171
+ from ab_core.template import placeholder_func
172
+
173
+
174
+ if __name__ == "__main__":
175
+ print("This is a placeholder: ", placeholdder_func())
176
+ ```
177
+
178
+ ______________________________________________________________________
179
+
180
+ ## Formatting and linting
181
+
182
+ We use Ruff as the formatter and linter. The pre-commit has hooks which runs
183
+ checking and applies linting automatically. The CI validates the linting,
184
+ ensuring main is always looking clean.
185
+
186
+ You can manually use these commands too:
187
+
188
+ 1. `make lint` - check for linting issues.
189
+ 1. `make format` - fix linting issues.
190
+
191
+ ______________________________________________________________________
192
+
193
+ ## CICD
194
+
195
+ ### Publishing to PyPI
196
+
197
+ We publish to PyPI using Github releases. Steps are as follows:
198
+
199
+ 1. Manually update the version in `pyproject.toml` file using a PR and merge to
200
+ main. Use `uv version --bump {patch/minor/major}` to update the version.
201
+ 1. Create a new release in Github with the tag name as the version number. This
202
+ will trigger the `publish` workflow. In the Release window, type in the
203
+ version number and it will prompt to create a new tag.
204
+ 1. Verify the release in
205
+ [PyPI](https://pypi.org/project/package-identity-context/)
@@ -0,0 +1,188 @@
1
+ <div align="center">
2
+
3
+ # Python Package Template
4
+
5
+ The template repository for creating python packages, shared across auth-broker.
6
+
7
+ ![Python](https://img.shields.io/badge/Python-3.12-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54)
8
+ ![UV](https://img.shields.io/badge/UV-Fast-6E40C9?style=for-the-badge)
9
+ ![Hatchling](https://img.shields.io/badge/Hatchling-PEP517-6E40C9?style=for-the-badge)
10
+ ![Ruff](https://img.shields.io/badge/Ruff-Lint-000000?style=for-the-badge)
11
+ ![Pre-commit](https://img.shields.io/badge/Pre--commit-Hooks-000000?style=for-the-badge)
12
+ ![Pytest](https://img.shields.io/badge/Pytest-Unit%2BAsync-08979C?style=for-the-badge)
13
+ ![Coverage](https://img.shields.io/badge/Cov-Reports-08979C?style=for-the-badge)
14
+ ![GitHub Actions](https://img.shields.io/badge/Actions-CI%2FCD-F7B500?style=for-the-badge&logo=github-actions)
15
+ ![PyPI](https://img.shields.io/badge/PyPI-Publish-6E40C9?style=for-the-badge)
16
+ ![Makefile](https://img.shields.io/badge/Makefile-Scripts-F7B500?style=for-the-badge)
17
+
18
+ 🦜🕸️
19
+
20
+ [![CI](https://github.com/auth-broker/package-template/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/auth-broker/package-template/actions/workflows/ci.yaml)
21
+
22
+ </div>
23
+
24
+ ______________________________________________________________________
25
+
26
+ ## Table of Contents
27
+
28
+ <!-- toc -->
29
+
30
+ - [Introduction](#introduction)
31
+ - [Quick Start](#quick-start)
32
+ - [Installation](#installation)
33
+ - [Usage](#usage)
34
+ - [Formatting and linting](#formatting-and-linting)
35
+ - [CICD](#cicd)
36
+
37
+ <!-- tocstop -->
38
+
39
+ ______________________________________________________________________
40
+
41
+ ## Introduction
42
+
43
+ This template repository aims to create a reusable package template which
44
+ streamlines the creation and publishing of isolated python packages in auth-broker.
45
+ This is aligned with the engineering vision @ auth-broker for better modularisation and
46
+ reusability of code.
47
+
48
+ ______________________________________________________________________
49
+
50
+ ## Quick Start
51
+
52
+ Since this is just a package, and not a service, there is no real "run" action.
53
+ But you can run the tests immediately.
54
+
55
+ Here are a list of available commands via make.
56
+
57
+ ### Bare Metal (i.e. your machine)
58
+
59
+ 1. `make install` - install the required dependencies.
60
+ 1. `make test` - runs the tests.
61
+
62
+ ### Docker
63
+
64
+ 1. `make build-docker` - build the docker image.
65
+ 1. `make run-docker` - run the docker compose services.
66
+ 1. `make test-docker` - run the tests in docker.
67
+ 1. `make clean-docker` - remove all docker containers etc.
68
+
69
+ ______________________________________________________________________
70
+
71
+ ## Installation
72
+
73
+ ### For Dev work on the repo
74
+
75
+ Install `uv`, (_if you haven't already_)
76
+ https://docs.astral.sh/uv/getting-started/installation/#installation-methods
77
+
78
+ ```shell
79
+ brew install uv
80
+ ```
81
+
82
+ Initialise pre-commit (validates ruff on commit.)
83
+
84
+ ```shell
85
+ uv run pre-commit install
86
+ ```
87
+
88
+ Install dependencies (including dev dependencies)
89
+
90
+ ```shell
91
+ uv sync
92
+ ```
93
+
94
+ If you are adding a new dev dependency, please run:
95
+
96
+ ```shell
97
+ uv add --dev {your-new-package}
98
+ ```
99
+
100
+ ### Namespaces
101
+
102
+ Packages all share the same namespace `ab_core`. To import this package into
103
+ your project:
104
+
105
+ ```python
106
+ from ab_core.template import placeholder_func
107
+ ```
108
+
109
+ We encourage you to make your package available to all of ab via this
110
+ `ab_core` namespace. The goal is to streamline development, POCs and overall
111
+ collaboration.
112
+
113
+ ______________________________________________________________________
114
+
115
+ ## Usage
116
+
117
+ ### Adding the dependency to your project
118
+
119
+ The library is available on PyPI. You can install it using the following
120
+ command:
121
+
122
+ **Using pip**:
123
+
124
+ ```shell
125
+ pip install package-identity-context
126
+ ```
127
+
128
+ **Using UV**
129
+
130
+ Note: there is currently no nice way like poetry, hence we still needd to
131
+ provide the full url. https://github.com/astral-sh/uv/issues/10140
132
+
133
+ Add the dependency
134
+
135
+ ```shell
136
+ uv add package-identity-context
137
+ ```
138
+
139
+ **Using poetry**:
140
+
141
+ Then run the following command to install the package:
142
+
143
+ ```shell
144
+ poetry add package-identity-context
145
+ ```
146
+
147
+ ### How tos
148
+
149
+ **Example Usage**
150
+
151
+ ```python
152
+ # Please update this based on your package!
153
+
154
+ from ab_core.template import placeholder_func
155
+
156
+
157
+ if __name__ == "__main__":
158
+ print("This is a placeholder: ", placeholdder_func())
159
+ ```
160
+
161
+ ______________________________________________________________________
162
+
163
+ ## Formatting and linting
164
+
165
+ We use Ruff as the formatter and linter. The pre-commit has hooks which runs
166
+ checking and applies linting automatically. The CI validates the linting,
167
+ ensuring main is always looking clean.
168
+
169
+ You can manually use these commands too:
170
+
171
+ 1. `make lint` - check for linting issues.
172
+ 1. `make format` - fix linting issues.
173
+
174
+ ______________________________________________________________________
175
+
176
+ ## CICD
177
+
178
+ ### Publishing to PyPI
179
+
180
+ We publish to PyPI using Github releases. Steps are as follows:
181
+
182
+ 1. Manually update the version in `pyproject.toml` file using a PR and merge to
183
+ main. Use `uv version --bump {patch/minor/major}` to update the version.
184
+ 1. Create a new release in Github with the tag name as the version number. This
185
+ will trigger the `publish` workflow. In the Release window, type in the
186
+ version number and it will prompt to create a new tag.
187
+ 1. Verify the release in
188
+ [PyPI](https://pypi.org/project/package-identity-context/)
@@ -0,0 +1,77 @@
1
+ [build-system]
2
+ build-backend = "hatchling.build"
3
+ requires = ["hatchling"]
4
+
5
+ [dependency-groups]
6
+ dev = [
7
+ "debugpy>=1.8.14,<2",
8
+ "isort>=6.0.1,<7",
9
+ "ab-test-fixtures>=0.1.3,<1",
10
+ "pre-commit>=4.2.0,<5",
11
+ "pytest-asyncio>=1.0.0,<2",
12
+ "pytest-cov>=6.2.1,<7",
13
+ "pytest>=8.4.1,<9",
14
+ "ruff>=0.12.3,<0.13",
15
+ "tox>=4.27.0,<5",
16
+ "create-prism-mockserver>=0.1.1"
17
+ ]
18
+
19
+ [project]
20
+ authors = [{email = "mattcoul7@gmail.com", name = "Matt Coulter"}]
21
+ dependencies = [
22
+ "ab-client-token-validator>=0.1.0",
23
+ "ab-client-user>=0.1.0",
24
+ "ab-dependency>=0.1.9",
25
+ "pydantic>=2.11.10",
26
+ ]
27
+ description = "Glue token validation + user service to provide identity context in FastAPI"
28
+ name = "ab-identity-context"
29
+ readme = "README.md"
30
+ requires-python = ">=3.12,<4.0"
31
+ version = "0.1.0"
32
+
33
+ [project.optional-dependencies]
34
+ all = [
35
+ "fastapi>=0.115.14",
36
+ ]
37
+ fastapi = ["fastapi>=0.115.14"]
38
+
39
+ [tool.hatch.build.targets.sdist]
40
+ include = ["src/ab_core"]
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ include = ["src/ab_core"]
44
+
45
+ [tool.hatch.build.targets.wheel.sources]
46
+ "src/ab_core" = "ab_core"
47
+
48
+ [tool.pytest.ini_options]
49
+ asyncio_default_fixture_loop_scope = "session"
50
+ asyncio_mode = "auto"
51
+ markers = [
52
+ "integration: Assigns the test to the integration test suite.",
53
+ "regression: Assigns the test to the regression test suite"
54
+ ]
55
+
56
+ [tool.ruff]
57
+ line-length = 120
58
+ src = ["src"]
59
+ target-version = "py312"
60
+
61
+ [tool.ruff.lint]
62
+ exclude = [".git", ".venv", "__pycache__", "proto"]
63
+ fixable = ["ALL"]
64
+ ignore = [
65
+ "D104" # missing docstring in public package
66
+ ]
67
+ select = [
68
+ "ARG001", # unused arguments
69
+ "B", # flake8-bugbear
70
+ "C4", # flake8-comprehensions
71
+ "D", # pydocstyle (docstring checks)
72
+ "E", # pycodestyle errors
73
+ "F", # pyflakes
74
+ "I", # isort
75
+ "UP", # pyupgrade
76
+ "W" # pycodestyle warnings
77
+ ]
@@ -0,0 +1,40 @@
1
+ """API Context Manager for IdentityContext."""
2
+
3
+ from typing import Annotated
4
+
5
+ try:
6
+ from fastapi import Header, HTTPException, status
7
+ except ImportError as e:
8
+ raise RuntimeError(
9
+ "`ab_core.identity_context.dependency::get_identity_context` requires FastAPI dependency."
10
+ " Please install ab-identity-context[fastapi] to use this module."
11
+ ) from e
12
+
13
+ from .exceptions import IdentificationError
14
+ from .identify import identify
15
+ from .models import IdentityContext
16
+
17
+
18
+ async def get_identity_context(
19
+ authorization: Annotated[str | None, Header()] = None,
20
+ ) -> IdentityContext:
21
+ """FastAPI dependency that returns IdentityContext.
22
+
23
+ Usage:
24
+ identity: Annotated[IdentityContext, Depends(identity_context)]
25
+ """
26
+ if not authorization or not authorization.lower().startswith("bearer "):
27
+ raise HTTPException(
28
+ status_code=status.HTTP_401_UNAUTHORIZED,
29
+ detail="Missing bearer token",
30
+ )
31
+ _, token = authorization.split(" ", 1)
32
+ try:
33
+ return await identify(
34
+ token=token,
35
+ )
36
+ except IdentificationError as e:
37
+ raise HTTPException(
38
+ status_code=status.HTTP_401_UNAUTHORIZED,
39
+ detail=str(e),
40
+ ) from e
@@ -0,0 +1,7 @@
1
+ """Exceptions."""
2
+
3
+
4
+ class IdentificationError(Exception):
5
+ """Arbitrary error when trying to identify a user, given a token."""
6
+
7
+ ...
@@ -0,0 +1,66 @@
1
+ """Sample entrypoint for template package."""
2
+
3
+ from typing import Annotated
4
+
5
+ from ab_client_token_validator import Client as TokenValidatorClient
6
+ from ab_client_token_validator.api.token_validator import validate_token_validate_post
7
+ from ab_client_token_validator.models import ValidateTokenRequest
8
+
9
+ from ab_client_user.api.user import upsert_user_by_oidc_user_oidc_put
10
+ from ab_client_user.client import Client as UserClient
11
+ from ab_client_user.models import UpsertByOIDCRequest
12
+
13
+ from ab_core.dependency import Depends, inject
14
+ from ab_core.dependency.loaders import ObjectLoaderEnvironment
15
+
16
+ from .exceptions import IdentificationError
17
+ from .models import IdentityContext, User, ValidatedOIDCClaims
18
+
19
+
20
+ @inject
21
+ async def identify(
22
+ token: str,
23
+ token_validator_client: Annotated[
24
+ TokenValidatorClient,
25
+ Depends(ObjectLoaderEnvironment[TokenValidatorClient](env_prefix="TOKEN_VALIDATOR_CLIENT"), persist=True),
26
+ ],
27
+ user_client: Annotated[
28
+ UserClient, Depends(ObjectLoaderEnvironment[UserClient](env_prefix="USER_CLIENT"), persist=True)
29
+ ],
30
+ ) -> IdentityContext:
31
+ """Identity a user given a valid token."""
32
+ # 1. validate the token
33
+ async with token_validator_client as token_validator_client:
34
+ claims = await validate_token_validate_post.asyncio(
35
+ client=token_validator_client,
36
+ body=ValidateTokenRequest(
37
+ token=token,
38
+ ),
39
+ )
40
+ if claims is None:
41
+ raise IdentificationError("Token validation failed.")
42
+ if type(claims).__name__ == "HTTPValidationError":
43
+ raise ValueError(f"Bad token validation request: {claims}")
44
+
45
+ # 2. upsert the user
46
+ async with user_client as user_client:
47
+ user = await upsert_user_by_oidc_user_oidc_put.asyncio(
48
+ client=user_client,
49
+ body=UpsertByOIDCRequest(
50
+ oidc_sub=claims.sub,
51
+ oidc_iss=claims.iss,
52
+ email=claims.email,
53
+ display_name=claims.given_name or claims.name or claims.nickname,
54
+ preferred_username=claims.nickname or claims.name or claims.given_name,
55
+ ),
56
+ )
57
+ if user is None:
58
+ raise IdentificationError("User validation failed.")
59
+ if type(user).__name__ == "HTTPValidationError":
60
+ raise ValueError(f"Bad user validation request: {user}")
61
+
62
+ return IdentityContext(
63
+ token=token,
64
+ claims=ValidatedOIDCClaims.model_validate(claims.to_dict()),
65
+ user=User.model_validate(user.to_dict()),
66
+ )
@@ -0,0 +1,18 @@
1
+ """Identity Context Models."""
2
+
3
+ from ab_client_token_validator.models import ValidatedOIDCClaims as AttrsOIDCClaims
4
+ from ab_client_user.models import User as AttrsUser
5
+ from pydantic import BaseModel, Field
6
+
7
+ from ab_core.dependency.pydanticize import pydanticize_type
8
+
9
+ User: type[BaseModel] = pydanticize_type(AttrsUser)
10
+ ValidatedOIDCClaims: type[BaseModel] = pydanticize_type(AttrsOIDCClaims)
11
+
12
+
13
+ class IdentityContext(BaseModel):
14
+ """Per-request identity context resolved from the bearer token."""
15
+
16
+ token: str = Field(..., description="Raw Bearer token")
17
+ claims: ValidatedOIDCClaims = Field(..., description="Claims from validated token")
18
+ user: User = Field(..., description="The current user")