test0001 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.
test0001-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Chris <goabonga@pm.me>
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.3
2
+ Name: test0001
3
+ Version: 0.1.0
4
+ Summary: OIDC/OAuth2 authentication service.
5
+ License: MIT
6
+ Keywords: oidc,oauth2,authentication,authorization,identity,openid-connect,jwt,token,sso,security
7
+ Author: Chris
8
+ Author-email: goabonga@pm.me
9
+ Requires-Python: >=3.10
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Topic :: Internet :: WWW/HTTP :: Session
22
+ Classifier: Topic :: Security
23
+ Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
24
+ Classifier: Typing :: Typed
25
+ Provides-Extra: server
26
+ Provides-Extra: worker
27
+ Requires-Dist: alembic ; extra == "server"
28
+ Requires-Dist: asyncpg ; extra == "server"
29
+ Requires-Dist: celery[redis] ; extra == "worker"
30
+ Requires-Dist: fastapi ; extra == "server"
31
+ Requires-Dist: jinja2 ; extra == "server"
32
+ Requires-Dist: pydantic-settings
33
+ Requires-Dist: sqlalchemy[asyncio] ; extra == "server"
34
+ Requires-Dist: uvicorn[standard] ; extra == "server"
35
+ Description-Content-Type: text/markdown
36
+
37
+ # Shomer
38
+
39
+ OIDC/OAuth2 authentication service.
40
+
41
+ ## Installation
42
+
43
+ ### CLI only
44
+
45
+ ```bash
46
+ pip install shomer
47
+ ```
48
+
49
+ ### CLI + Server
50
+
51
+ ```bash
52
+ pip install shomer[server]
53
+ ```
54
+
55
+ ### CLI + Worker
56
+
57
+ ```bash
58
+ pip install shomer[worker]
59
+ ```
60
+
61
+ ### All components
62
+
63
+ ```bash
64
+ pip install shomer[server,worker]
65
+ ```
66
+
67
+ ### Docker
68
+
69
+ ```bash
70
+ docker pull ghcr.io/goabonga/shomer-server:latest
71
+ docker run -p 8000:8000 ghcr.io/goabonga/shomer-server:latest
72
+ ```
73
+
74
+ ### Docker Compose
75
+
76
+ ```bash
77
+ git clone https://github.com/goabonga/shomer.git
78
+ cd shomer
79
+ make docker-up
80
+ ```
81
+
82
+ ### Helm
83
+
84
+ ```bash
85
+ helm install shomer oci://ghcr.io/goabonga/shomer-chart/shomer --version 0.1.0
86
+ ```
87
+
88
+ ## Development
89
+
90
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full contribution guide.
91
+
92
+ ## License
93
+
94
+ [MIT](LICENSE)
95
+
@@ -0,0 +1,58 @@
1
+ # Shomer
2
+
3
+ OIDC/OAuth2 authentication service.
4
+
5
+ ## Installation
6
+
7
+ ### CLI only
8
+
9
+ ```bash
10
+ pip install shomer
11
+ ```
12
+
13
+ ### CLI + Server
14
+
15
+ ```bash
16
+ pip install shomer[server]
17
+ ```
18
+
19
+ ### CLI + Worker
20
+
21
+ ```bash
22
+ pip install shomer[worker]
23
+ ```
24
+
25
+ ### All components
26
+
27
+ ```bash
28
+ pip install shomer[server,worker]
29
+ ```
30
+
31
+ ### Docker
32
+
33
+ ```bash
34
+ docker pull ghcr.io/goabonga/shomer-server:latest
35
+ docker run -p 8000:8000 ghcr.io/goabonga/shomer-server:latest
36
+ ```
37
+
38
+ ### Docker Compose
39
+
40
+ ```bash
41
+ git clone https://github.com/goabonga/shomer.git
42
+ cd shomer
43
+ make docker-up
44
+ ```
45
+
46
+ ### Helm
47
+
48
+ ```bash
49
+ helm install shomer oci://ghcr.io/goabonga/shomer-chart/shomer --version 0.1.0
50
+ ```
51
+
52
+ ## Development
53
+
54
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full contribution guide.
55
+
56
+ ## License
57
+
58
+ [MIT](LICENSE)
@@ -0,0 +1,188 @@
1
+ [tool.poetry]
2
+ name = "test0001"
3
+ version = "0.1.0"
4
+ description = "OIDC/OAuth2 authentication service."
5
+ authors = ["Chris <goabonga@pm.me>"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ packages = [
9
+ { include = "shomer", from = "src" }
10
+ ]
11
+ include = [
12
+ "src/shomer/py.typed"
13
+ ]
14
+ exclude = [
15
+ "tests",
16
+ "scripts",
17
+ "docs",
18
+ "Makefile"
19
+ ]
20
+ keywords = [
21
+ "oidc",
22
+ "oauth2",
23
+ "authentication",
24
+ "authorization",
25
+ "identity",
26
+ "openid-connect",
27
+ "jwt",
28
+ "token",
29
+ "sso",
30
+ "security"
31
+ ]
32
+ classifiers = [
33
+ "Development Status :: 3 - Alpha",
34
+ "Intended Audience :: Developers",
35
+ "License :: OSI Approved :: MIT License",
36
+ "Natural Language :: English",
37
+ "Operating System :: OS Independent",
38
+ "Programming Language :: Python :: 3",
39
+ "Programming Language :: Python :: 3 :: Only",
40
+ "Programming Language :: Python :: 3.10",
41
+ "Programming Language :: Python :: 3.11",
42
+ "Programming Language :: Python :: 3.12",
43
+ "Programming Language :: Python :: 3.13",
44
+ "Topic :: Security",
45
+ "Topic :: Internet :: WWW/HTTP :: Session",
46
+ "Topic :: System :: Systems Administration :: Authentication/Directory",
47
+ "Typing :: Typed"
48
+ ]
49
+
50
+ [tool.poetry.dependencies]
51
+ python = ">=3.10"
52
+ pydantic-settings = "*"
53
+ fastapi = {version = "*", optional = true}
54
+ uvicorn = {extras = ["standard"], version = "*", optional = true}
55
+ jinja2 = {version = "*", optional = true}
56
+ sqlalchemy = {extras = ["asyncio"], version = "*", optional = true}
57
+ asyncpg = {version = "*", optional = true}
58
+ alembic = {version = "*", optional = true}
59
+ celery = {extras = ["redis"], version = "*", optional = true}
60
+
61
+ [tool.poetry.extras]
62
+ server = ["fastapi", "uvicorn", "jinja2", "sqlalchemy", "asyncpg", "alembic"]
63
+ worker = ["celery"]
64
+
65
+ [tool.poetry.group.dev]
66
+ optional = true
67
+
68
+ [tool.poetry.group.dev.dependencies]
69
+ commitizen = "*"
70
+ pytest = "*"
71
+ pytest-cov = "*"
72
+ ruff = "*"
73
+ isort = "*"
74
+ mypy = "*"
75
+
76
+ [tool.poetry.group.test]
77
+ optional = true
78
+
79
+ [tool.poetry.group.test.dependencies]
80
+ behave = "*"
81
+ playwright = "*"
82
+
83
+ [tool.poetry.group.favicon]
84
+ optional = true
85
+
86
+ [tool.poetry.group.favicon.dependencies]
87
+ cairosvg = "*"
88
+ Pillow = "*"
89
+
90
+ [tool.poetry.group.docs]
91
+ optional = true
92
+
93
+ [tool.poetry.group.docs.dependencies]
94
+ mkdocs = "*"
95
+ mkdocs-material = "*"
96
+ mkdocs-gen-files = "*"
97
+ mkdocs-autorefs = "*"
98
+ mkdocstrings = {extras = ["python"], version = "*"}
99
+ mkdocs-macros-plugin = "*"
100
+
101
+ [tool.poetry.scripts]
102
+ shomer = "shomer.cli:main"
103
+
104
+ [tool.behave]
105
+ paths = ["features"]
106
+
107
+ [[tool.poetry-auto-export.exports]]
108
+ output = "requirements.txt"
109
+ without_hashes = true
110
+
111
+ [[tool.poetry-auto-export.exports]]
112
+ output = "requirements-server.txt"
113
+ without_hashes = true
114
+ extras = ["server"]
115
+
116
+ [[tool.poetry-auto-export.exports]]
117
+ output = "requirements-worker.txt"
118
+ without_hashes = true
119
+ extras = ["worker"]
120
+
121
+ [[tool.poetry-auto-export.exports]]
122
+ output = "requirements-dev.txt"
123
+ without_hashes = true
124
+ with = ["dev", "docs", "test"]
125
+ extras = ["server", "worker"]
126
+
127
+ [build-system]
128
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
129
+ build-backend = "poetry.core.masonry.api"
130
+
131
+ [tool.commitizen]
132
+ name = "cz_conventional_commits"
133
+ version = "0.1.0"
134
+ tag_format = "v$version"
135
+ update_changelog_on_bump = true
136
+ changelog_file = "CHANGELOG.md"
137
+ changelog_incremental = true
138
+ major_version_zero = true
139
+ version_files = [
140
+ "pyproject.toml:version",
141
+ "src/shomer/__init__.py",
142
+ "tests/test_version.py:__expected_version__",
143
+ "features/steps/ui_steps.py:__expected_version__",
144
+ "chart/shomer/Chart.yaml:version",
145
+ "chart/shomer/Chart.yaml:appVersion",
146
+ "README.md:--version",
147
+ "mkdocs.yml:version"
148
+ ]
149
+ gpg_sign = true
150
+
151
+ [tool.pytest.ini_options]
152
+ minversion = "7.0"
153
+ addopts = "--cov=src/shomer --cov-report=term --cov-report=xml --cov-report=html --junitxml=junit.xml -o junit_family=legacy"
154
+ testpaths = ["tests"]
155
+
156
+ [tool.coverage.run]
157
+ omit = [
158
+ "tests/*"
159
+ ]
160
+
161
+ [tool.coverage.report]
162
+ exclude_lines = [
163
+ "pragma: no cover",
164
+ "if TYPE_CHECKING:",
165
+ "if __name__ == .__main__.:",
166
+ "raise NotImplementedError",
167
+ "pass # abstract",
168
+ "\\.\\.\\.",
169
+ "except ImportError",
170
+ ]
171
+
172
+ [tool.isort]
173
+ profile = "black"
174
+ src_paths = ["src", "tests"]
175
+
176
+ [tool.ruff]
177
+ line-length = 88
178
+ src = ["src", "tests"]
179
+ exclude = ["__pycache__"]
180
+
181
+ [tool.mypy]
182
+ python_version = "3.11"
183
+ strict = true
184
+ files = ["src", "tests"]
185
+
186
+ [[tool.mypy.overrides]]
187
+ module = ["celery", "celery.*"]
188
+ ignore_missing_imports = true
@@ -0,0 +1,9 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ __version__: str = "0.1.0"
5
+ """Current version of the shomer package."""
6
+
7
+ __all__ = [
8
+ "__version__",
9
+ ]
@@ -0,0 +1,64 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """FastAPI application for the Shomer authentication service."""
5
+
6
+ import asyncio
7
+ from contextlib import asynccontextmanager
8
+ from pathlib import Path
9
+ from typing import AsyncIterator
10
+
11
+ from fastapi import FastAPI
12
+ from fastapi.staticfiles import StaticFiles
13
+ from fastapi.templating import Jinja2Templates
14
+
15
+ from . import __version__
16
+ from .core.settings import get_settings
17
+
18
+ settings = get_settings()
19
+
20
+ state: dict[str, str] = {"status": "starting"}
21
+
22
+
23
+ @asynccontextmanager
24
+ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
25
+ """Application lifespan handler."""
26
+ from shomer.core.database import engine
27
+
28
+ await asyncio.sleep(settings.startup_delay)
29
+ state["status"] = "ready"
30
+ yield
31
+ state["status"] = "shutting_down"
32
+ await engine.dispose()
33
+
34
+
35
+ def create_app() -> FastAPI:
36
+ """Create and configure the FastAPI application."""
37
+ application = FastAPI(
38
+ title="Shomer",
39
+ description="OIDC/OAuth2 authentication service.",
40
+ version=__version__,
41
+ docs_url=None,
42
+ redoc_url=None,
43
+ lifespan=lifespan,
44
+ )
45
+
46
+ application.mount(
47
+ "/static",
48
+ StaticFiles(directory=str(Path(__file__).parent / "static")),
49
+ name="static",
50
+ )
51
+
52
+ from shomer.routes.docs import router as docs_router
53
+ from shomer.routes.health import router as health_router
54
+ from shomer.routes.views import router as views_router
55
+
56
+ application.include_router(health_router)
57
+ application.include_router(docs_router)
58
+ application.include_router(views_router)
59
+
60
+ return application
61
+
62
+
63
+ templates = Jinja2Templates(directory=str(Path(__file__).parent / "templates"))
64
+ app = create_app()
@@ -0,0 +1,23 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """Command-line interface for the Shomer authentication service."""
5
+
6
+ import argparse
7
+
8
+ from shomer import __version__
9
+
10
+
11
+ def main() -> None:
12
+ """Parse command-line arguments and execute the CLI."""
13
+ parser = argparse.ArgumentParser(
14
+ prog="shomer", description="OIDC/OAuth2 authentication service."
15
+ )
16
+ parser.add_argument(
17
+ "--version", action="version", version=f"%(prog)s v{__version__}"
18
+ )
19
+ parser.parse_args()
20
+
21
+
22
+ if __name__ == "__main__":
23
+ main()
@@ -0,0 +1,4 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """Core modules."""
@@ -0,0 +1,26 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """Async SQLAlchemy database setup."""
5
+
6
+ from collections.abc import AsyncIterator
7
+
8
+ from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
9
+ from sqlalchemy.orm import DeclarativeBase
10
+
11
+ from shomer.core.settings import get_settings
12
+
13
+ settings = get_settings()
14
+
15
+ engine = create_async_engine(settings.database_url, echo=False)
16
+ async_session = async_sessionmaker(engine, expire_on_commit=False)
17
+
18
+
19
+ class Base(DeclarativeBase):
20
+ """Base class for SQLAlchemy models."""
21
+
22
+
23
+ async def get_db() -> AsyncIterator[AsyncSession]: # pragma: no cover
24
+ """Yield an async database session."""
25
+ async with async_session() as session:
26
+ yield session
@@ -0,0 +1,42 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """Application settings using pydantic-settings."""
5
+
6
+ from functools import lru_cache
7
+
8
+ from pydantic_settings import BaseSettings, SettingsConfigDict
9
+
10
+
11
+ class Settings(BaseSettings):
12
+ """Application configuration loaded from environment variables."""
13
+
14
+ model_config = SettingsConfigDict(env_prefix="SHOMER_", env_file=".env")
15
+
16
+ # Server
17
+ host: str = "0.0.0.0"
18
+ port: int = 8000
19
+ startup_delay: float = 1.0
20
+
21
+ # Database
22
+ database_url: str = "postgresql+asyncpg://shomer:shomer@localhost:5432/shomer"
23
+
24
+ # Celery
25
+ celery_broker_url: str = "redis://localhost:6379/0"
26
+ celery_result_backend: str = ""
27
+
28
+ @property
29
+ def celery_backend(self) -> str:
30
+ """Return result backend, defaulting to broker URL."""
31
+ return self.celery_result_backend or self.celery_broker_url
32
+
33
+ @property
34
+ def database_url_sync(self) -> str:
35
+ """Return sync database URL for Alembic."""
36
+ return self.database_url.replace("+asyncpg", "")
37
+
38
+
39
+ @lru_cache
40
+ def get_settings() -> Settings:
41
+ """Return cached settings instance."""
42
+ return Settings()
File without changes
@@ -0,0 +1,4 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """FastAPI route modules."""
@@ -0,0 +1,32 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """Documentation routes."""
5
+
6
+ from fastapi import APIRouter, Request
7
+ from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
8
+ from fastapi.responses import HTMLResponse
9
+
10
+ router = APIRouter()
11
+
12
+ FAVICON_URL = "/static/favicon.ico"
13
+
14
+
15
+ @router.get("/docs", include_in_schema=False)
16
+ def docs(request: Request) -> HTMLResponse:
17
+ """Render the Swagger UI documentation."""
18
+ return get_swagger_ui_html(
19
+ openapi_url=request.app.openapi_url,
20
+ title=f"{request.app.title} - Docs",
21
+ swagger_favicon_url=FAVICON_URL,
22
+ )
23
+
24
+
25
+ @router.get("/redoc", include_in_schema=False)
26
+ def redoc(request: Request) -> HTMLResponse:
27
+ """Render the ReDoc documentation."""
28
+ return get_redoc_html(
29
+ openapi_url=request.app.openapi_url,
30
+ title=f"{request.app.title} - ReDoc",
31
+ redoc_favicon_url=FAVICON_URL,
32
+ )
@@ -0,0 +1,34 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """Health check routes."""
5
+
6
+ from fastapi import APIRouter
7
+ from fastapi.responses import JSONResponse
8
+
9
+ router = APIRouter()
10
+
11
+
12
+ # Imported at function level to avoid circular imports
13
+ def _get_state() -> dict[str, str]:
14
+ from shomer.app import state
15
+
16
+ return state
17
+
18
+
19
+ @router.get("/liveness", include_in_schema=False)
20
+ def liveness() -> JSONResponse:
21
+ """Liveness probe: is the process alive? (k8s livenessProbe / docker HEALTHCHECK)."""
22
+ state = _get_state()
23
+ if state["status"] == "shutting_down":
24
+ return JSONResponse({"status": state["status"]}, status_code=503)
25
+ return JSONResponse({"status": state["status"]}, status_code=200)
26
+
27
+
28
+ @router.get("/readiness", include_in_schema=False)
29
+ def readiness() -> JSONResponse:
30
+ """Readiness probe: is the app ready to serve traffic? (k8s readinessProbe)."""
31
+ state = _get_state()
32
+ if state["status"] != "ready":
33
+ return JSONResponse({"status": state["status"]}, status_code=503)
34
+ return JSONResponse({"status": state["status"]}, status_code=200)
@@ -0,0 +1,19 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """HTML view routes."""
5
+
6
+ from fastapi import APIRouter, Request
7
+ from fastapi.responses import HTMLResponse
8
+
9
+ from shomer import __version__
10
+
11
+ router = APIRouter()
12
+
13
+
14
+ @router.get("/", response_class=HTMLResponse, include_in_schema=False)
15
+ def index(request: Request) -> HTMLResponse:
16
+ """Render the home page."""
17
+ from shomer.app import templates
18
+
19
+ return templates.TemplateResponse(request, "index.html", {"version": __version__})
@@ -0,0 +1,4 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """Celery tasks for the Shomer authentication service."""
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Shomer</title>
7
+ <link rel="icon" href="/static/favicon.ico" type="image/x-icon">
8
+ </head>
9
+ <body>
10
+ <h1>Shomer</h1>
11
+ <p>OIDC/OAuth2 authentication service.</p>
12
+ <p>v{{ version }}</p>
13
+ </body>
14
+ </html>
@@ -0,0 +1,29 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 Chris <goabonga@pm.me>
3
+
4
+ """Celery worker for the Shomer authentication service."""
5
+
6
+ from celery import Celery
7
+
8
+ from shomer.core.settings import get_settings
9
+
10
+ settings = get_settings()
11
+
12
+ app = Celery(
13
+ "shomer",
14
+ broker=settings.celery_broker_url,
15
+ backend=settings.celery_backend,
16
+ )
17
+
18
+ app.config_from_object(
19
+ {
20
+ "task_serializer": "json",
21
+ "result_serializer": "json",
22
+ "accept_content": ["json"],
23
+ "timezone": "UTC",
24
+ "enable_utc": True,
25
+ "beat_schedule": {},
26
+ }
27
+ )
28
+
29
+ app.autodiscover_tasks(["shomer.tasks"])